Merge "Add support for common encryption" into jb-mr2-dev
diff --git a/Android.mk b/Android.mk
index 2ad7a72..e70f9f3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -69,7 +69,6 @@
core/java/android/app/IAlarmManager.aidl \
core/java/android/app/IBackupAgent.aidl \
core/java/android/app/IInstrumentationWatcher.aidl \
- core/java/android/app/INotificationListener.aidl \
core/java/android/app/INotificationManager.aidl \
core/java/android/app/IProcessObserver.aidl \
core/java/android/app/ISearchManager.aidl \
@@ -148,6 +147,7 @@
core/java/android/os/IUpdateLock.aidl \
core/java/android/os/IUserManager.aidl \
core/java/android/os/IVibratorService.aidl \
+ core/java/android/service/notification/INotificationListener.aidl \
core/java/android/service/dreams/IDreamManager.aidl \
core/java/android/service/dreams/IDreamService.aidl \
core/java/android/service/wallpaper/IWallpaperConnection.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index fc63866..4debdc2 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -159,6 +159,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodSession.*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/current.txt b/api/current.txt
index 31acb35..1e1f01c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22,6 +22,7 @@
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
+ field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
@@ -2477,6 +2478,11 @@
method public void setPropertyName(java.lang.String);
}
+ public class RectEvaluator implements android.animation.TypeEvaluator {
+ ctor public RectEvaluator();
+ method public android.graphics.Rect evaluate(float, android.graphics.Rect, android.graphics.Rect);
+ }
+
public class TimeAnimator extends android.animation.ValueAnimator {
ctor public TimeAnimator();
method public void setTimeListener(android.animation.TimeAnimator.TimeListener);
@@ -2589,6 +2595,10 @@
method public abstract void setDisplayShowHomeEnabled(boolean);
method public abstract void setDisplayShowTitleEnabled(boolean);
method public abstract void setDisplayUseLogoEnabled(boolean);
+ method public void setHomeActionContentDescription(java.lang.CharSequence);
+ method public void setHomeActionContentDescription(int);
+ method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
+ method public void setHomeAsUpIndicator(int);
method public void setHomeButtonEnabled(boolean);
method public abstract void setIcon(int);
method public abstract void setIcon(android.graphics.drawable.Drawable);
@@ -4851,7 +4861,7 @@
}
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.BluetoothGatt connectGatt(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();
@@ -4860,6 +4870,7 @@
method public android.bluetooth.BluetoothClass getBluetoothClass();
method public int getBondState();
method public java.lang.String getName();
+ method public int getType();
method public android.os.ParcelUuid[] getUuids();
method public void writeToParcel(android.os.Parcel, int);
field public static final java.lang.String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
@@ -4874,6 +4885,10 @@
field public static final int BOND_BONDING = 11; // 0xb
field public static final int BOND_NONE = 10; // 0xa
field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int DEVICE_TYPE_CLASSIC = 1; // 0x1
+ field public static final int DEVICE_TYPE_DUAL = 3; // 0x3
+ field public static final int DEVICE_TYPE_LE = 2; // 0x2
+ field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
field public static final int ERROR = -2147483648; // 0x80000000
field public static final java.lang.String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
field public static final java.lang.String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
@@ -4887,11 +4902,14 @@
public final class BluetoothGatt implements android.bluetooth.BluetoothProfile {
method public void abortReliableWrite(android.bluetooth.BluetoothDevice);
method public boolean beginReliableWrite();
+ method public void close();
+ method public boolean connect();
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 android.bluetooth.BluetoothDevice getDevice();
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();
@@ -4914,15 +4932,15 @@
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);
+ method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
+ method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+ method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+ method public void onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int);
+ method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+ method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+ method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
+ method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
+ method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
}
public class BluetoothGattCharacteristic {
@@ -16509,7 +16527,7 @@
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();
@@ -16564,9 +16582,9 @@
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 {
@@ -16940,6 +16958,7 @@
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();
@@ -18485,10 +18504,6 @@
protected static abstract interface ContactsContract.DataUsageStatColumns {
field public static final java.lang.String LAST_TIME_USED = "last_time_used";
field public static final java.lang.String TIMES_USED = "times_used";
- field public static final java.lang.String USAGE_TYPE = "usage_type";
- field public static final int USAGE_TYPE_CALL = 0; // 0x0
- field public static final int USAGE_TYPE_LONG_TEXT = 1; // 0x1
- field public static final int USAGE_TYPE_SHORT_TEXT = 2; // 0x2
}
public static final class ContactsContract.Directory implements android.provider.BaseColumns {
@@ -19908,29 +19923,6 @@
method public void skip(int);
}
- public deprecated class FileA3D extends android.renderscript.BaseObj {
- method public static deprecated android.renderscript.FileA3D createFromAsset(android.renderscript.RenderScript, android.content.res.AssetManager, java.lang.String);
- method public static deprecated android.renderscript.FileA3D createFromFile(android.renderscript.RenderScript, java.lang.String);
- method public static deprecated android.renderscript.FileA3D createFromFile(android.renderscript.RenderScript, java.io.File);
- method public static deprecated android.renderscript.FileA3D createFromResource(android.renderscript.RenderScript, android.content.res.Resources, int);
- method public deprecated android.renderscript.FileA3D.IndexEntry getIndexEntry(int);
- method public deprecated int getIndexEntryCount();
- }
-
- public static final deprecated class FileA3D.EntryType extends java.lang.Enum {
- method public static android.renderscript.FileA3D.EntryType valueOf(java.lang.String);
- method public static final android.renderscript.FileA3D.EntryType[] values();
- enum_constant public static final deprecated android.renderscript.FileA3D.EntryType MESH;
- enum_constant public static final deprecated android.renderscript.FileA3D.EntryType UNKNOWN;
- }
-
- public static deprecated class FileA3D.IndexEntry {
- method public deprecated android.renderscript.FileA3D.EntryType getEntryType();
- method public deprecated android.renderscript.Mesh getMesh();
- method public deprecated java.lang.String getName();
- method public deprecated android.renderscript.BaseObj getObject();
- }
-
public class Float2 {
ctor public Float2();
ctor public Float2(float, float);
@@ -19955,23 +19947,6 @@
field public float z;
}
- public deprecated class Font extends android.renderscript.BaseObj {
- method public static deprecated android.renderscript.Font create(android.renderscript.RenderScript, android.content.res.Resources, java.lang.String, android.renderscript.Font.Style, float);
- method public static deprecated android.renderscript.Font createFromAsset(android.renderscript.RenderScript, android.content.res.Resources, java.lang.String, float);
- method public static deprecated android.renderscript.Font createFromFile(android.renderscript.RenderScript, android.content.res.Resources, java.lang.String, float);
- method public static deprecated android.renderscript.Font createFromFile(android.renderscript.RenderScript, android.content.res.Resources, java.io.File, float);
- method public static deprecated android.renderscript.Font createFromResource(android.renderscript.RenderScript, android.content.res.Resources, int, float);
- }
-
- public static final deprecated class Font.Style extends java.lang.Enum {
- method public static android.renderscript.Font.Style valueOf(java.lang.String);
- method public static final android.renderscript.Font.Style[] values();
- enum_constant public static final deprecated android.renderscript.Font.Style BOLD;
- enum_constant public static final deprecated android.renderscript.Font.Style BOLD_ITALIC;
- enum_constant public static final deprecated android.renderscript.Font.Style ITALIC;
- enum_constant public static final deprecated android.renderscript.Font.Style NORMAL;
- }
-
public class Int2 {
ctor public Int2();
ctor public Int2(int, int);
@@ -20086,245 +20061,6 @@
method public void transpose();
}
- public deprecated class Mesh extends android.renderscript.BaseObj {
- method public deprecated android.renderscript.Allocation getIndexSetAllocation(int);
- method public deprecated android.renderscript.Mesh.Primitive getPrimitive(int);
- method public deprecated int getPrimitiveCount();
- method public deprecated android.renderscript.Allocation getVertexAllocation(int);
- method public deprecated int getVertexAllocationCount();
- }
-
- public static deprecated class Mesh.AllocationBuilder {
- ctor public deprecated Mesh.AllocationBuilder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.Mesh.AllocationBuilder addIndexSetAllocation(android.renderscript.Allocation, android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.AllocationBuilder addIndexSetType(android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.AllocationBuilder addVertexAllocation(android.renderscript.Allocation) throws java.lang.IllegalStateException;
- method public deprecated android.renderscript.Mesh create();
- method public deprecated int getCurrentIndexSetIndex();
- method public deprecated int getCurrentVertexTypeIndex();
- }
-
- public static deprecated class Mesh.Builder {
- ctor public deprecated Mesh.Builder(android.renderscript.RenderScript, int);
- method public deprecated android.renderscript.Mesh.Builder addIndexSetType(android.renderscript.Type, android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.Builder addIndexSetType(android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.Builder addIndexSetType(android.renderscript.Element, int, android.renderscript.Mesh.Primitive);
- method public deprecated android.renderscript.Mesh.Builder addVertexType(android.renderscript.Type) throws java.lang.IllegalStateException;
- method public deprecated android.renderscript.Mesh.Builder addVertexType(android.renderscript.Element, int) throws java.lang.IllegalStateException;
- method public deprecated android.renderscript.Mesh create();
- method public deprecated int getCurrentIndexSetIndex();
- method public deprecated int getCurrentVertexTypeIndex();
- }
-
- public static final deprecated class Mesh.Primitive extends java.lang.Enum {
- method public static android.renderscript.Mesh.Primitive valueOf(java.lang.String);
- method public static final android.renderscript.Mesh.Primitive[] values();
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive LINE;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive LINE_STRIP;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive POINT;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive TRIANGLE;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive TRIANGLE_FAN;
- enum_constant public static final deprecated android.renderscript.Mesh.Primitive TRIANGLE_STRIP;
- }
-
- public static deprecated class Mesh.TriangleMeshBuilder {
- ctor public deprecated Mesh.TriangleMeshBuilder(android.renderscript.RenderScript, int, int);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder addTriangle(int, int, int);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder addVertex(float, float);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder addVertex(float, float, float);
- method public deprecated android.renderscript.Mesh create(boolean);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder setColor(float, float, float, float);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder setNormal(float, float, float);
- method public deprecated android.renderscript.Mesh.TriangleMeshBuilder setTexture(float, float);
- field public static final deprecated int COLOR = 1; // 0x1
- field public static final deprecated int NORMAL = 2; // 0x2
- field public static final deprecated int TEXTURE_0 = 256; // 0x100
- }
-
- public class Program extends android.renderscript.BaseObj {
- method public void bindConstants(android.renderscript.Allocation, int);
- method public void bindSampler(android.renderscript.Sampler, int) throws java.lang.IllegalArgumentException;
- method public void bindTexture(android.renderscript.Allocation, int) throws java.lang.IllegalArgumentException;
- method public android.renderscript.Type getConstant(int);
- method public int getConstantCount();
- method public int getTextureCount();
- method public java.lang.String getTextureName(int);
- method public android.renderscript.Program.TextureType getTextureType(int);
- }
-
- public static class Program.BaseProgramBuilder {
- ctor protected Program.BaseProgramBuilder(android.renderscript.RenderScript);
- method public android.renderscript.Program.BaseProgramBuilder addConstant(android.renderscript.Type) throws java.lang.IllegalStateException;
- method public android.renderscript.Program.BaseProgramBuilder addTexture(android.renderscript.Program.TextureType) throws java.lang.IllegalArgumentException;
- method public android.renderscript.Program.BaseProgramBuilder addTexture(android.renderscript.Program.TextureType, java.lang.String) throws java.lang.IllegalArgumentException;
- method public int getCurrentConstantIndex();
- method public int getCurrentTextureIndex();
- method protected void initProgram(android.renderscript.Program);
- method public android.renderscript.Program.BaseProgramBuilder setShader(java.lang.String);
- method public android.renderscript.Program.BaseProgramBuilder setShader(android.content.res.Resources, int);
- }
-
- public static final class Program.TextureType extends java.lang.Enum {
- method public static android.renderscript.Program.TextureType valueOf(java.lang.String);
- method public static final android.renderscript.Program.TextureType[] values();
- enum_constant public static final android.renderscript.Program.TextureType TEXTURE_2D;
- enum_constant public static final android.renderscript.Program.TextureType TEXTURE_CUBE;
- }
-
- public deprecated class ProgramFragment extends android.renderscript.Program {
- }
-
- public static deprecated class ProgramFragment.Builder extends android.renderscript.Program.BaseProgramBuilder {
- ctor public deprecated ProgramFragment.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramFragment create();
- }
-
- public deprecated class ProgramFragmentFixedFunction extends android.renderscript.ProgramFragment {
- }
-
- public static deprecated class ProgramFragmentFixedFunction.Builder {
- ctor public deprecated ProgramFragmentFixedFunction.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramFragmentFixedFunction create();
- method public deprecated android.renderscript.ProgramFragmentFixedFunction.Builder setPointSpriteTexCoordinateReplacement(boolean);
- method public deprecated android.renderscript.ProgramFragmentFixedFunction.Builder setTexture(android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode, android.renderscript.ProgramFragmentFixedFunction.Builder.Format, int) throws java.lang.IllegalArgumentException;
- method public deprecated android.renderscript.ProgramFragmentFixedFunction.Builder setVaryingColor(boolean);
- field public static final deprecated int MAX_TEXTURE = 2; // 0x2
- }
-
- public static final deprecated class ProgramFragmentFixedFunction.Builder.EnvMode extends java.lang.Enum {
- method public static android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode valueOf(java.lang.String);
- method public static final android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode[] values();
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode DECAL;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode MODULATE;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.EnvMode REPLACE;
- }
-
- public static final deprecated class ProgramFragmentFixedFunction.Builder.Format extends java.lang.Enum {
- method public static android.renderscript.ProgramFragmentFixedFunction.Builder.Format valueOf(java.lang.String);
- method public static final android.renderscript.ProgramFragmentFixedFunction.Builder.Format[] values();
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.Format ALPHA;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.Format LUMINANCE_ALPHA;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.Format RGB;
- enum_constant public static final deprecated android.renderscript.ProgramFragmentFixedFunction.Builder.Format RGBA;
- }
-
- public deprecated class ProgramRaster extends android.renderscript.BaseObj {
- method public static deprecated android.renderscript.ProgramRaster CULL_BACK(android.renderscript.RenderScript);
- method public static deprecated android.renderscript.ProgramRaster CULL_FRONT(android.renderscript.RenderScript);
- method public static deprecated android.renderscript.ProgramRaster CULL_NONE(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramRaster.CullMode getCullMode();
- method public deprecated boolean isPointSpriteEnabled();
- }
-
- public static deprecated class ProgramRaster.Builder {
- ctor public deprecated ProgramRaster.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramRaster create();
- method public deprecated android.renderscript.ProgramRaster.Builder setCullMode(android.renderscript.ProgramRaster.CullMode);
- method public deprecated android.renderscript.ProgramRaster.Builder setPointSpriteEnabled(boolean);
- }
-
- public static final deprecated class ProgramRaster.CullMode extends java.lang.Enum {
- method public static android.renderscript.ProgramRaster.CullMode valueOf(java.lang.String);
- method public static final android.renderscript.ProgramRaster.CullMode[] values();
- enum_constant public static final deprecated android.renderscript.ProgramRaster.CullMode BACK;
- enum_constant public static final deprecated android.renderscript.ProgramRaster.CullMode FRONT;
- enum_constant public static final deprecated android.renderscript.ProgramRaster.CullMode NONE;
- }
-
- public class ProgramStore extends android.renderscript.BaseObj {
- method public static android.renderscript.ProgramStore BLEND_ALPHA_DEPTH_NONE(android.renderscript.RenderScript);
- method public static android.renderscript.ProgramStore BLEND_ALPHA_DEPTH_TEST(android.renderscript.RenderScript);
- method public static android.renderscript.ProgramStore BLEND_NONE_DEPTH_NONE(android.renderscript.RenderScript);
- method public static android.renderscript.ProgramStore BLEND_NONE_DEPTH_TEST(android.renderscript.RenderScript);
- method public android.renderscript.ProgramStore.BlendDstFunc getBlendDstFunc();
- method public android.renderscript.ProgramStore.BlendSrcFunc getBlendSrcFunc();
- method public android.renderscript.ProgramStore.DepthFunc getDepthFunc();
- method public boolean isColorMaskAlphaEnabled();
- method public boolean isColorMaskBlueEnabled();
- method public boolean isColorMaskGreenEnabled();
- method public boolean isColorMaskRedEnabled();
- method public boolean isDepthMaskEnabled();
- method public boolean isDitherEnabled();
- }
-
- public static final class ProgramStore.BlendDstFunc extends java.lang.Enum {
- method public static android.renderscript.ProgramStore.BlendDstFunc valueOf(java.lang.String);
- method public static final android.renderscript.ProgramStore.BlendDstFunc[] values();
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc DST_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ONE;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ONE_MINUS_DST_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ONE_MINUS_SRC_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ONE_MINUS_SRC_COLOR;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc SRC_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc SRC_COLOR;
- enum_constant public static final android.renderscript.ProgramStore.BlendDstFunc ZERO;
- }
-
- public static final class ProgramStore.BlendSrcFunc extends java.lang.Enum {
- method public static android.renderscript.ProgramStore.BlendSrcFunc valueOf(java.lang.String);
- method public static final android.renderscript.ProgramStore.BlendSrcFunc[] values();
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc DST_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc DST_COLOR;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ONE;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ONE_MINUS_DST_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ONE_MINUS_DST_COLOR;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ONE_MINUS_SRC_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc SRC_ALPHA;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc SRC_ALPHA_SATURATE;
- enum_constant public static final android.renderscript.ProgramStore.BlendSrcFunc ZERO;
- }
-
- public static class ProgramStore.Builder {
- ctor public ProgramStore.Builder(android.renderscript.RenderScript);
- method public android.renderscript.ProgramStore create();
- method public android.renderscript.ProgramStore.Builder setBlendFunc(android.renderscript.ProgramStore.BlendSrcFunc, android.renderscript.ProgramStore.BlendDstFunc);
- method public android.renderscript.ProgramStore.Builder setColorMaskEnabled(boolean, boolean, boolean, boolean);
- method public android.renderscript.ProgramStore.Builder setDepthFunc(android.renderscript.ProgramStore.DepthFunc);
- method public android.renderscript.ProgramStore.Builder setDepthMaskEnabled(boolean);
- method public android.renderscript.ProgramStore.Builder setDitherEnabled(boolean);
- }
-
- public static final class ProgramStore.DepthFunc extends java.lang.Enum {
- method public static android.renderscript.ProgramStore.DepthFunc valueOf(java.lang.String);
- method public static final android.renderscript.ProgramStore.DepthFunc[] values();
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc ALWAYS;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc EQUAL;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc GREATER;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc GREATER_OR_EQUAL;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc LESS;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc LESS_OR_EQUAL;
- enum_constant public static final android.renderscript.ProgramStore.DepthFunc NOT_EQUAL;
- }
-
- public deprecated class ProgramVertex extends android.renderscript.Program {
- method public deprecated android.renderscript.Element getInput(int);
- method public deprecated int getInputCount();
- }
-
- public static deprecated class ProgramVertex.Builder extends android.renderscript.Program.BaseProgramBuilder {
- ctor public deprecated ProgramVertex.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramVertex.Builder addInput(android.renderscript.Element) throws java.lang.IllegalStateException;
- method public deprecated android.renderscript.ProgramVertex create();
- }
-
- public deprecated class ProgramVertexFixedFunction extends android.renderscript.ProgramVertex {
- method public deprecated void bindConstants(android.renderscript.ProgramVertexFixedFunction.Constants);
- }
-
- public static deprecated class ProgramVertexFixedFunction.Builder {
- ctor public deprecated ProgramVertexFixedFunction.Builder(android.renderscript.RenderScript);
- method public deprecated android.renderscript.ProgramVertexFixedFunction create();
- method public deprecated android.renderscript.ProgramVertexFixedFunction.Builder setTextureMatrixEnable(boolean);
- }
-
- public static deprecated class ProgramVertexFixedFunction.Constants {
- ctor public deprecated ProgramVertexFixedFunction.Constants(android.renderscript.RenderScript);
- method public deprecated void destroy();
- method public deprecated void setModelview(android.renderscript.Matrix4f);
- method public deprecated void setProjection(android.renderscript.Matrix4f);
- method public deprecated void setTexture(android.renderscript.Matrix4f);
- }
-
public class RSDriverException extends android.renderscript.RSRuntimeException {
ctor public RSDriverException(java.lang.String);
}
@@ -20341,35 +20077,6 @@
ctor public RSRuntimeException(java.lang.String);
}
- public deprecated class RSSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback {
- ctor public deprecated RSSurfaceView(android.content.Context);
- ctor public deprecated RSSurfaceView(android.content.Context, android.util.AttributeSet);
- method public deprecated android.renderscript.RenderScriptGL createRenderScriptGL(android.renderscript.RenderScriptGL.SurfaceConfig);
- method public deprecated void destroyRenderScriptGL();
- method public deprecated android.renderscript.RenderScriptGL getRenderScriptGL();
- method public deprecated void pause();
- method public deprecated void resume();
- method public deprecated void setRenderScriptGL(android.renderscript.RenderScriptGL);
- method public deprecated void surfaceChanged(android.view.SurfaceHolder, int, int, int);
- method public deprecated void surfaceCreated(android.view.SurfaceHolder);
- method public deprecated void surfaceDestroyed(android.view.SurfaceHolder);
- }
-
- public deprecated class RSTextureView extends android.view.TextureView implements android.view.TextureView.SurfaceTextureListener {
- ctor public deprecated RSTextureView(android.content.Context);
- ctor public deprecated RSTextureView(android.content.Context, android.util.AttributeSet);
- method public deprecated android.renderscript.RenderScriptGL createRenderScriptGL(android.renderscript.RenderScriptGL.SurfaceConfig);
- method public deprecated void destroyRenderScriptGL();
- method public deprecated android.renderscript.RenderScriptGL getRenderScriptGL();
- method public deprecated void onSurfaceTextureAvailable(android.graphics.SurfaceTexture, int, int);
- method public deprecated boolean onSurfaceTextureDestroyed(android.graphics.SurfaceTexture);
- method public deprecated void onSurfaceTextureSizeChanged(android.graphics.SurfaceTexture, int, int);
- method public deprecated void onSurfaceTextureUpdated(android.graphics.SurfaceTexture);
- method public deprecated void pause();
- method public deprecated void resume();
- method public deprecated void setRenderScriptGL(android.renderscript.RenderScriptGL);
- }
-
public class RenderScript {
method public void contextDump();
method public static android.renderscript.RenderScript create(android.content.Context);
@@ -20413,30 +20120,6 @@
field protected int mLength;
}
- public deprecated class RenderScriptGL extends android.renderscript.RenderScript {
- ctor public deprecated RenderScriptGL(android.content.Context, android.renderscript.RenderScriptGL.SurfaceConfig);
- method public deprecated void bindProgramFragment(android.renderscript.ProgramFragment);
- method public deprecated void bindProgramRaster(android.renderscript.ProgramRaster);
- method public deprecated void bindProgramStore(android.renderscript.ProgramStore);
- method public deprecated void bindProgramVertex(android.renderscript.ProgramVertex);
- method public deprecated void bindRootScript(android.renderscript.Script);
- method public deprecated int getHeight();
- method public deprecated int getWidth();
- method public deprecated void pause();
- method public deprecated void resume();
- method public deprecated void setSurface(android.view.SurfaceHolder, int, int);
- method public deprecated void setSurfaceTexture(android.graphics.SurfaceTexture, int, int);
- }
-
- public static deprecated class RenderScriptGL.SurfaceConfig {
- ctor public deprecated RenderScriptGL.SurfaceConfig();
- ctor public deprecated RenderScriptGL.SurfaceConfig(android.renderscript.RenderScriptGL.SurfaceConfig);
- method public deprecated void setAlpha(int, int);
- method public deprecated void setColor(int, int);
- method public deprecated void setDepth(int, int);
- method public deprecated void setSamples(int, int, float);
- }
-
public class Sampler extends android.renderscript.BaseObj {
method public static android.renderscript.Sampler CLAMP_LINEAR(android.renderscript.RenderScript);
method public static android.renderscript.Sampler CLAMP_LINEAR_MIP_LINEAR(android.renderscript.RenderScript);
@@ -20750,12 +20433,27 @@
package android.security {
+ public class AndroidKeyPairGeneratorSpec implements java.security.spec.AlgorithmParameterSpec {
+ }
+
+ public static class AndroidKeyPairGeneratorSpec.Builder {
+ ctor public AndroidKeyPairGeneratorSpec.Builder(android.content.Context);
+ method public android.security.AndroidKeyPairGeneratorSpec build();
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setAlias(java.lang.String);
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setEndDate(java.util.Date);
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setSerialNumber(java.math.BigInteger);
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setStartDate(java.util.Date);
+ method public android.security.AndroidKeyPairGeneratorSpec.Builder setSubject(javax.security.auth.x500.X500Principal);
+ }
+
public final class KeyChain {
ctor public KeyChain();
method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
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";
@@ -20823,6 +20521,38 @@
}
+package android.service.notification {
+
+ public abstract class NotificationListenerService extends android.app.Service {
+ ctor public NotificationListenerService();
+ method public final void clearAllNotifications();
+ method public final void clearNotification(java.lang.String, java.lang.String, int);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification);
+ method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
+ }
+
+ public class StatusBarNotification implements android.os.Parcelable {
+ ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
+ ctor public StatusBarNotification(android.os.Parcel);
+ method public android.service.notification.StatusBarNotification clone();
+ method public int describeContents();
+ method public int getUserId();
+ method public boolean isClearable();
+ method public boolean isOngoing();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public final int id;
+ field public final android.app.Notification notification;
+ field public final java.lang.String pkg;
+ field public final long postTime;
+ field public final java.lang.String tag;
+ field public final android.os.UserHandle user;
+ }
+
+}
+
package android.service.textservice {
public abstract class SpellCheckerService extends android.app.Service {
@@ -21446,6 +21176,7 @@
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();
@@ -22803,6 +22534,7 @@
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);
@@ -24472,6 +24204,7 @@
method public float getMax();
method public float getMin();
method public float getRange();
+ method public float getResolution();
method public int getSource();
method public boolean isFromSource(int);
}
@@ -25229,14 +24962,6 @@
field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
}
- public abstract interface Overlay {
- method public abstract void add(android.graphics.drawable.Drawable);
- method public abstract void add(android.view.View);
- method public abstract void clear();
- method public abstract void remove(android.graphics.drawable.Drawable);
- method public abstract void remove(android.view.View);
- }
-
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
method public float getCurrentSpan();
@@ -25541,7 +25266,7 @@
method public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
method public int getOverScrollMode();
- method public android.view.Overlay getOverlay();
+ method public android.view.ViewOverlay getOverlay();
method public int getPaddingBottom();
method public int getPaddingEnd();
method public int getPaddingLeft();
@@ -26282,12 +26007,23 @@
method public abstract void onChildViewRemoved(android.view.View, android.view.View);
}
+ public class ViewGroupOverlay extends android.view.ViewOverlay {
+ method public void add(android.view.View);
+ method public void remove(android.view.View);
+ }
+
public abstract interface ViewManager {
method public abstract void addView(android.view.View, android.view.ViewGroup.LayoutParams);
method public abstract void removeView(android.view.View);
method public abstract void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams);
}
+ public class ViewOverlay {
+ method public void add(android.graphics.drawable.Drawable);
+ method public void clear();
+ method public void remove(android.graphics.drawable.Drawable);
+ }
+
public abstract interface ViewParent {
method public abstract void bringChildToFront(android.view.View);
method public abstract void childDrawableStateChanged(android.view.View);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 1c02960..ccb9e1f 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -39,6 +39,7 @@
import android.os.UserHandle;
import android.util.AndroidException;
import android.view.IWindowManager;
+import com.android.internal.os.BaseCommand;
import java.io.BufferedReader;
import java.io.File;
@@ -50,12 +51,9 @@
import java.util.HashSet;
import java.util.List;
-public class Am {
+public class Am extends BaseCommand {
private IActivityManager mAm;
- private String[] mArgs;
- private int mNextArg;
- private String mCurArgData;
private int mStartFlags = 0;
private boolean mWaitOption = false;
@@ -67,33 +65,155 @@
private String mProfileFile;
- // These are magic strings understood by the Eclipse plugin.
- private static final String FATAL_ERROR_CODE = "Error type 1";
- private static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
- private static final String NO_CLASS_ERROR_CODE = "Error type 3";
-
/**
* Command-line entry point.
*
* @param args The command-line arguments
*/
public static void main(String[] args) {
- try {
- (new Am()).run(args);
- } catch (IllegalArgumentException e) {
- showUsage();
- System.err.println("Error: " + e.getMessage());
- } catch (Exception e) {
- e.printStackTrace(System.err);
- System.exit(1);
- }
+ (new Am()).run(args);
}
- private void run(String[] args) throws Exception {
- if (args.length < 1) {
- showUsage();
- return;
- }
+ public void onShowUsage(PrintStream out) {
+ out.println(
+ "usage: am [subcommand] [options]\n" +
+ "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
+ " [--R COUNT] [-S] [--opengl-trace]\n" +
+ " [--user <USER_ID> | current] <INTENT>\n" +
+ " am startservice [--user <USER_ID> | current] <INTENT>\n" +
+ " am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
+ " am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
+ " am kill-all\n" +
+ " am broadcast [--user <USER_ID> | all | current] <INTENT>\n" +
+ " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
+ " [--user <USER_ID> | current]\n" +
+ " [--no-window-animation] <COMPONENT>\n" +
+ " am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" +
+ " am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
+ " am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
+ " am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
+ " am clear-debug-app\n" +
+ " am monitor [--gdb <port>]\n" +
+ " am screen-compat [on|off] <PACKAGE>\n" +
+ " am to-uri [INTENT]\n" +
+ " am to-intent-uri [INTENT]\n" +
+ " am switch-user <USER_ID>\n" +
+ " am stop-user <USER_ID>\n" +
+ "\n" +
+ "am start: start an Activity. Options are:\n" +
+ " -D: enable debugging\n" +
+ " -W: wait for launch to complete\n" +
+ " --start-profiler <FILE>: start profiler and send results to <FILE>\n" +
+ " -P <FILE>: like above, but profiling stops when app goes idle\n" +
+ " -R: repeat the activity launch <COUNT> times. Prior to each repeat,\n" +
+ " the top activity will be finished.\n" +
+ " -S: force stop the target app before starting the activity\n" +
+ " --opengl-trace: enable tracing of OpenGL functions\n" +
+ " --user <USER_ID> | current: Specify which user to run as; if not\n" +
+ " specified then run as the current user.\n" +
+ "\n" +
+ "am startservice: start a Service. Options are:\n" +
+ " --user <USER_ID> | current: Specify which user to run as; if not\n" +
+ " specified then run as the current user.\n" +
+ "\n" +
+ "am force-stop: force stop everything associated with <PACKAGE>.\n" +
+ " --user <USER_ID> | all | current: Specify user to force stop;\n" +
+ " all users if not specified.\n" +
+ "\n" +
+ "am kill: Kill all processes associated with <PACKAGE>. Only kills.\n" +
+ " processes that are safe to kill -- that is, will not impact the user\n" +
+ " experience.\n" +
+ " --user <USER_ID> | all | current: Specify user whose processes to kill;\n" +
+ " all users if not specified.\n" +
+ "\n" +
+ "am kill-all: Kill all background processes.\n" +
+ "\n" +
+ "am broadcast: send a broadcast Intent. Options are:\n" +
+ " --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
+ " specified then send to all users.\n" +
+ " --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
+ "\n" +
+ "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" +
+ " is the form <TEST_PACKAGE>/<RUNNER_CLASS>. Options are:\n" +
+ " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with\n" +
+ " [-e perf true] to generate raw output for performance measurements.\n" +
+ " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" +
+ " common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
+ " -p <FILE>: write profiling data to <FILE>\n" +
+ " -w: wait for instrumentation to finish before returning. Required for\n" +
+ " test runners.\n" +
+ " --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
+ " current user if not specified.\n" +
+ " --no-window-animation: turn off window animations will running.\n" +
+ "\n" +
+ "am profile: start and stop profiler on a process. The given <PROCESS> argument\n" +
+ " may be either a process name or pid. Options are:\n" +
+ " --user <USER_ID> | current: When supplying a process name,\n" +
+ " specify user of process to profile; uses current user if not specified.\n" +
+ "\n" +
+ "am dumpheap: dump the heap of a process. The given <PROCESS> argument may\n" +
+ " be either a process name or pid. Options are:\n" +
+ " -n: dump native heap instead of managed heap\n" +
+ " --user <USER_ID> | current: When supplying a process name,\n" +
+ " specify user of process to dump; uses current user if not specified.\n" +
+ "\n" +
+ "am set-debug-app: set application <PACKAGE> to debug. Options are:\n" +
+ " -w: wait for debugger when application starts\n" +
+ " --persistent: retain this value\n" +
+ "\n" +
+ "am clear-debug-app: clear the previously set-debug-app.\n" +
+ "\n" +
+ "am bug-report: request bug report generation; will launch UI\n" +
+ " when done to select where it should be delivered.\n" +
+ "\n" +
+ "am monitor: start monitoring for crashes or ANRs.\n" +
+ " --gdb: start gdbserv on the given port at crash/ANR\n" +
+ "\n" +
+ "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
+ "\n" +
+ "am to-uri: print the given Intent specification as a URI.\n" +
+ "\n" +
+ "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
+ "\n" +
+ "am switch-user: switch to put USER_ID in the foreground, starting\n" +
+ " execution of that user if it is currently stopped.\n" +
+ "\n" +
+ "am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
+ " code until a later explicit switch to it.\n" +
+ "\n" +
+ "<INTENT> specifications include these flags and arguments:\n" +
+ " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
+ " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
+ " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
+ " [--esn <EXTRA_KEY> ...]\n" +
+ " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
+ " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
+ " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
+ " [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]\n" +
+ " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
+ " [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]\n" +
+ " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
+ " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
+ " [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
+ " [-n <COMPONENT>] [-f <FLAGS>]\n" +
+ " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
+ " [--debug-log-resolution] [--exclude-stopped-packages]\n" +
+ " [--include-stopped-packages]\n" +
+ " [--activity-brought-to-front] [--activity-clear-top]\n" +
+ " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
+ " [--activity-launched-from-history] [--activity-multiple-task]\n" +
+ " [--activity-no-animation] [--activity-no-history]\n" +
+ " [--activity-no-user-action] [--activity-previous-is-top]\n" +
+ " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
+ " [--activity-single-top] [--activity-clear-task]\n" +
+ " [--activity-task-on-home]\n" +
+ " [--receiver-registered-only] [--receiver-replace-pending]\n" +
+ " [--selector]\n" +
+ " [<URI> | <PACKAGE> | <COMPONENT>]\n"
+ );
+ }
+
+ public void onRun() throws Exception {
mAm = ActivityManagerNative.getDefault();
if (mAm == null) {
@@ -101,9 +221,7 @@
throw new AndroidException("Can't connect to activity manager; is the system running?");
}
- mArgs = args;
- String op = args[0];
- mNextArg = 1;
+ String op = nextArgRequired();
if (op.equals("start")) {
runStart();
@@ -142,7 +260,7 @@
} else if (op.equals("stop-user")) {
runStopUser();
} else {
- throw new IllegalArgumentException("Unknown command: " + op);
+ showError("Error: unknown command '" + op + "'");
}
}
@@ -1303,193 +1421,4 @@
return true;
}
}
-
- private String nextOption() {
- if (mCurArgData != null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
- }
- if (mNextArg >= mArgs.length) {
- return null;
- }
- String arg = mArgs[mNextArg];
- if (!arg.startsWith("-")) {
- return null;
- }
- mNextArg++;
- if (arg.equals("--")) {
- return null;
- }
- if (arg.length() > 1 && arg.charAt(1) != '-') {
- if (arg.length() > 2) {
- mCurArgData = arg.substring(2);
- return arg.substring(0, 2);
- } else {
- mCurArgData = null;
- return arg;
- }
- }
- mCurArgData = null;
- return arg;
- }
-
- private String nextArg() {
- if (mCurArgData != null) {
- String arg = mCurArgData;
- mCurArgData = null;
- return arg;
- } else if (mNextArg < mArgs.length) {
- return mArgs[mNextArg++];
- } else {
- return null;
- }
- }
-
- private String nextArgRequired() {
- String arg = nextArg();
- if (arg == null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
- }
- return arg;
- }
-
- private static void showUsage() {
- System.err.println(
- "usage: am [subcommand] [options]\n" +
- "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
- " [--R COUNT] [-S] [--opengl-trace]\n" +
- " [--user <USER_ID> | current] <INTENT>\n" +
- " am startservice [--user <USER_ID> | current] <INTENT>\n" +
- " am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
- " am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
- " am kill-all\n" +
- " am broadcast [--user <USER_ID> | all | current] <INTENT>\n" +
- " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
- " [--user <USER_ID> | current]\n" +
- " [--no-window-animation] <COMPONENT>\n" +
- " am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" +
- " am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
- " am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
- " am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
- " am clear-debug-app\n" +
- " am monitor [--gdb <port>]\n" +
- " am screen-compat [on|off] <PACKAGE>\n" +
- " am to-uri [INTENT]\n" +
- " am to-intent-uri [INTENT]\n" +
- " am switch-user <USER_ID>\n" +
- " am stop-user <USER_ID>\n" +
- "\n" +
- "am start: start an Activity. Options are:\n" +
- " -D: enable debugging\n" +
- " -W: wait for launch to complete\n" +
- " --start-profiler <FILE>: start profiler and send results to <FILE>\n" +
- " -P <FILE>: like above, but profiling stops when app goes idle\n" +
- " -R: repeat the activity launch <COUNT> times. Prior to each repeat,\n" +
- " the top activity will be finished.\n" +
- " -S: force stop the target app before starting the activity\n" +
- " --opengl-trace: enable tracing of OpenGL functions\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- "\n" +
- "am startservice: start a Service. Options are:\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- "\n" +
- "am force-stop: force stop everything associated with <PACKAGE>.\n" +
- " --user <USER_ID> | all | current: Specify user to force stop;\n" +
- " all users if not specified.\n" +
- "\n" +
- "am kill: Kill all processes associated with <PACKAGE>. Only kills.\n" +
- " processes that are safe to kill -- that is, will not impact the user\n" +
- " experience.\n" +
- " --user <USER_ID> | all | current: Specify user whose processes to kill;\n" +
- " all users if not specified.\n" +
- "\n" +
- "am kill-all: Kill all background processes.\n" +
- "\n" +
- "am broadcast: send a broadcast Intent. Options are:\n" +
- " --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
- " specified then send to all users.\n" +
- " --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
- "\n" +
- "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" +
- " is the form <TEST_PACKAGE>/<RUNNER_CLASS>. Options are:\n" +
- " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with\n" +
- " [-e perf true] to generate raw output for performance measurements.\n" +
- " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" +
- " common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
- " -p <FILE>: write profiling data to <FILE>\n" +
- " -w: wait for instrumentation to finish before returning. Required for\n" +
- " test runners.\n" +
- " --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
- " current user if not specified.\n" +
- " --no-window-animation: turn off window animations will running.\n" +
- "\n" +
- "am profile: start and stop profiler on a process. The given <PROCESS> argument\n" +
- " may be either a process name or pid. Options are:\n" +
- " --user <USER_ID> | current: When supplying a process name,\n" +
- " specify user of process to profile; uses current user if not specified.\n" +
- "\n" +
- "am dumpheap: dump the heap of a process. The given <PROCESS> argument may\n" +
- " be either a process name or pid. Options are:\n" +
- " -n: dump native heap instead of managed heap\n" +
- " --user <USER_ID> | current: When supplying a process name,\n" +
- " specify user of process to dump; uses current user if not specified.\n" +
- "\n" +
- "am set-debug-app: set application <PACKAGE> to debug. Options are:\n" +
- " -w: wait for debugger when application starts\n" +
- " --persistent: retain this value\n" +
- "\n" +
- "am clear-debug-app: clear the previously set-debug-app.\n" +
- "\n" +
- "am bug-report: request bug report generation; will launch UI\n" +
- " when done to select where it should be delivered.\n" +
- "\n" +
- "am monitor: start monitoring for crashes or ANRs.\n" +
- " --gdb: start gdbserv on the given port at crash/ANR\n" +
- "\n" +
- "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
- "\n" +
- "am to-uri: print the given Intent specification as a URI.\n" +
- "\n" +
- "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
- "\n" +
- "am switch-user: switch to put USER_ID in the foreground, starting\n" +
- " execution of that user if it is currently stopped.\n" +
- "\n" +
- "am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
- " code until a later explicit switch to it.\n" +
- "\n" +
- "<INTENT> specifications include these flags and arguments:\n" +
- " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
- " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
- " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
- " [--esn <EXTRA_KEY> ...]\n" +
- " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
- " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
- " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
- " [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]\n" +
- " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
- " [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]\n" +
- " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
- " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
- " [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
- " [-n <COMPONENT>] [-f <FLAGS>]\n" +
- " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
- " [--debug-log-resolution] [--exclude-stopped-packages]\n" +
- " [--include-stopped-packages]\n" +
- " [--activity-brought-to-front] [--activity-clear-top]\n" +
- " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
- " [--activity-launched-from-history] [--activity-multiple-task]\n" +
- " [--activity-no-animation] [--activity-no-history]\n" +
- " [--activity-no-user-action] [--activity-previous-is-top]\n" +
- " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
- " [--activity-single-top] [--activity-clear-task]\n" +
- " [--activity-task-on-home]\n" +
- " [--receiver-registered-only] [--receiver-replace-pending]\n" +
- " [--selector]\n" +
- " [<URI> | <PACKAGE> | <COMPONENT>]\n"
- );
- }
}
diff --git a/cmds/media/Android.mk b/cmds/media/Android.mk
new file mode 100644
index 0000000..b9451c5
--- /dev/null
+++ b/cmds/media/Android.mk
@@ -0,0 +1,15 @@
+# Copyright 2013 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := media_cmd
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := media
+LOCAL_SRC_FILES := media
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
diff --git a/cmds/media/MODULE_LICENSE_APACHE2 b/cmds/media/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/media/MODULE_LICENSE_APACHE2
diff --git a/cmds/media/NOTICE b/cmds/media/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/cmds/media/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/cmds/media/media b/cmds/media/media
new file mode 100755
index 0000000..1194442
--- /dev/null
+++ b/cmds/media/media
@@ -0,0 +1,6 @@
+# Script to start "media_cmd" on the device, which has a very rudimentary
+# shell.
+#
+base=/system
+export CLASSPATH=$base/framework/media_cmd.jar
+exec app_process $base/bin com.android.commands.media.Media "$@"
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
new file mode 100644
index 0000000..56af7d6
--- /dev/null
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -0,0 +1,220 @@
+/*
+**
+** 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.
+*/
+
+package com.android.commands.media;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.IAudioService;
+import android.media.IRemoteControlDisplay;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.AndroidException;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import com.android.internal.os.BaseCommand;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+
+public class Media extends BaseCommand {
+
+ private IAudioService mAudioService;
+
+ /**
+ * Command-line entry point.
+ *
+ * @param args The command-line arguments
+ */
+ public static void main(String[] args) {
+ (new Media()).run(args);
+ }
+
+ public void onShowUsage(PrintStream out) {
+ out.println(
+ "usage: media [subcommand] [options]\n" +
+ " media dispatch KEY\n" +
+ " media remote-display\n" +
+ "\n" +
+ "media dispatch: dispatch a media key to the current media client.\n" +
+ " KEY may be: play, pause, play-pause, mute, headsethook,\n" +
+ " stop, next, previous, rewind, recordm fast-forword.\n" +
+ "media remote-display: monitor remote display updates.\n"
+ );
+ }
+
+ public void onRun() throws Exception {
+ mAudioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
+ Context.AUDIO_SERVICE));
+ if (mAudioService == null) {
+ System.err.println(NO_SYSTEM_ERROR_CODE);
+ throw new AndroidException("Can't connect to audio service; is the system running?");
+ }
+
+ String op = nextArgRequired();
+
+ if (op.equals("dispatch")) {
+ runDispatch();
+ } else if (op.equals("remote-display")) {
+ runRemoteDisplay();
+ } else {
+ showError("Error: unknown command '" + op + "'");
+ return;
+ }
+ }
+
+ private void sendMediaKey(KeyEvent event) {
+ try {
+ mAudioService.dispatchMediaKeyEvent(event);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void runDispatch() throws Exception {
+ String cmd = nextArgRequired();
+ int keycode;
+ if ("play".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_PLAY;
+ } else if ("pause".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_PAUSE;
+ } else if ("play-pause".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
+ } else if ("mute".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MUTE;
+ } else if ("headsethook".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_HEADSETHOOK;
+ } else if ("stop".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_STOP;
+ } else if ("next".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_NEXT;
+ } else if ("previous".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+ } else if ("rewind".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_REWIND;
+ } else if ("record".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_RECORD;
+ } else if ("fast-forward".equals(cmd)) {
+ keycode = KeyEvent.KEYCODE_MEDIA_FAST_FORWARD;
+ } else {
+ showError("Error: unknown dispatch code '" + cmd + "'");
+ return;
+ }
+
+ final long now = SystemClock.uptimeMillis();
+ sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+ sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_UP, keycode, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+ }
+
+ class RemoteDisplayMonitor extends IRemoteControlDisplay.Stub {
+ RemoteDisplayMonitor() {
+ }
+
+
+ @Override
+ public void setCurrentClientId(int clientGeneration, PendingIntent clientMediaIntent,
+ boolean clearing) {
+ System.out.println("New client: id=" + clientGeneration
+ + " intent=" + clientMediaIntent + " clearing=" + clearing);
+ }
+
+ @Override
+ public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+ long currentPosMs, float speed) {
+ System.out.println("New state: id=" + generationId + " state=" + state
+ + " time=" + stateChangeTimeMs + " pos=" + currentPosMs + " speed=" + speed);
+ }
+
+ @Override
+ public void setTransportControlInfo(int generationId, int transportControlFlags,
+ int posCapabilities) {
+ System.out.println("New control info: id=" + generationId
+ + " flags=0x" + Integer.toHexString(transportControlFlags)
+ + " cap=0x" + Integer.toHexString(posCapabilities));
+ }
+
+ @Override
+ public void setMetadata(int generationId, Bundle metadata) {
+ System.out.println("New metadata: id=" + generationId
+ + " data=" + metadata);
+ }
+
+ @Override
+ public void setArtwork(int generationId, Bitmap artwork) {
+ System.out.println("New artwork: id=" + generationId
+ + " art=" + artwork);
+ }
+
+ @Override
+ public void setAllMetadata(int generationId, Bundle metadata, Bitmap artwork) {
+ System.out.println("New metadata+artwork: id=" + generationId
+ + " data=" + metadata + " art=" + artwork);
+ }
+
+ void printUsageMessage() {
+ System.out.println("Monitoring remote control displays... available commands:");
+ System.out.println("(q)uit: finish monitoring");
+ }
+
+ void run() throws RemoteException {
+ printUsageMessage();
+
+ mAudioService.registerRemoteControlDisplay(this, 0, 0);
+
+ try {
+ InputStreamReader converter = new InputStreamReader(System.in);
+ BufferedReader in = new BufferedReader(converter);
+ String line;
+
+ while ((line = in.readLine()) != null) {
+ boolean addNewline = true;
+ if (line.length() <= 0) {
+ addNewline = false;
+ } else if ("q".equals(line) || "quit".equals(line)) {
+ break;
+ } else {
+ System.out.println("Invalid command: " + line);
+ }
+
+ synchronized (this) {
+ if (addNewline) {
+ System.out.println("");
+ }
+ printUsageMessage();
+ }
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ mAudioService.unregisterRemoteControlDisplay(this);
+ }
+ }
+ }
+
+ private void runRemoteDisplay() throws Exception {
+ RemoteDisplayMonitor monitor = new RemoteDisplayMonitor();
+ monitor.run();
+ }
+}
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index 31eba96..815a0ac 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -26,21 +26,15 @@
import android.util.AndroidException;
import android.view.Display;
import android.view.IWindowManager;
+import com.android.internal.os.BaseCommand;
+import java.io.PrintStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-public class Wm {
+public class Wm extends BaseCommand {
private IWindowManager mWm;
- private String[] mArgs;
- private int mNextArg;
- private String mCurArgData;
-
- // These are magic strings understood by the Eclipse plugin.
- private static final String FATAL_ERROR_CODE = "Error type 1";
- private static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
- private static final String NO_CLASS_ERROR_CODE = "Error type 3";
/**
* Command-line entry point.
@@ -48,23 +42,25 @@
* @param args The command-line arguments
*/
public static void main(String[] args) {
- try {
- (new Wm()).run(args);
- } catch (IllegalArgumentException e) {
- showUsage();
- System.err.println("Error: " + e.getMessage());
- } catch (Exception e) {
- e.printStackTrace(System.err);
- System.exit(1);
- }
+ (new Wm()).run(args);
}
- private void run(String[] args) throws Exception {
- if (args.length < 1) {
- showUsage();
- return;
- }
+ public void onShowUsage(PrintStream out) {
+ out.println(
+ "usage: wm [subcommand] [options]\n" +
+ " wm size [reset|WxH]\n" +
+ " wm density [reset|DENSITY]\n" +
+ " wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
+ "\n" +
+ "wm size: return or override display size.\n" +
+ "\n" +
+ "wm density: override display density.\n" +
+ "\n" +
+ "wm overscan: set overscan area for display.\n"
+ );
+ }
+ public void onRun() throws Exception {
mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
Context.WINDOW_SERVICE));
if (mWm == null) {
@@ -72,9 +68,7 @@
throw new AndroidException("Can't connect to window manager; is the system running?");
}
- mArgs = args;
- String op = args[0];
- mNextArg = 1;
+ String op = nextArgRequired();
if (op.equals("size")) {
runDisplaySize();
@@ -83,7 +77,8 @@
} else if (op.equals("overscan")) {
runDisplayOverscan();
} else {
- throw new IllegalArgumentException("Unknown command: " + op);
+ showError("Error: unknown command '" + op + "'");
+ return;
}
}
@@ -198,69 +193,4 @@
} catch (RemoteException e) {
}
}
-
- private String nextOption() {
- if (mCurArgData != null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
- }
- if (mNextArg >= mArgs.length) {
- return null;
- }
- String arg = mArgs[mNextArg];
- if (!arg.startsWith("-")) {
- return null;
- }
- mNextArg++;
- if (arg.equals("--")) {
- return null;
- }
- if (arg.length() > 1 && arg.charAt(1) != '-') {
- if (arg.length() > 2) {
- mCurArgData = arg.substring(2);
- return arg.substring(0, 2);
- } else {
- mCurArgData = null;
- return arg;
- }
- }
- mCurArgData = null;
- return arg;
- }
-
- private String nextArg() {
- if (mCurArgData != null) {
- String arg = mCurArgData;
- mCurArgData = null;
- return arg;
- } else if (mNextArg < mArgs.length) {
- return mArgs[mNextArg++];
- } else {
- return null;
- }
- }
-
- private String nextArgRequired() {
- String arg = nextArg();
- if (arg == null) {
- String prev = mArgs[mNextArg - 1];
- throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
- }
- return arg;
- }
-
- private static void showUsage() {
- System.err.println(
- "usage: wm [subcommand] [options]\n" +
- " wm size [reset|WxH]\n" +
- " wm density [reset|DENSITY]\n" +
- " wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
- "\n" +
- "wm size: return or override display size.\n" +
- "\n" +
- "wm density: override display density.\n" +
- "\n" +
- "wm overscan: set overscan area for display.\n"
- );
- }
}
diff --git a/core/java/android/animation/RectEvaluator.java b/core/java/android/animation/RectEvaluator.java
new file mode 100644
index 0000000..28d496b
--- /dev/null
+++ b/core/java/android/animation/RectEvaluator.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 android.animation;
+
+import android.graphics.Rect;
+
+/**
+ * This evaluator can be used to perform type interpolation between <code>Rect</code> values.
+ */
+public class RectEvaluator implements TypeEvaluator<Rect> {
+
+ /**
+ * This function returns the result of linearly interpolating the start and
+ * end Rect values, with <code>fraction</code> representing the proportion
+ * between the start and end values. The calculation is a simple parametric
+ * calculation on each of the separate components in the Rect objects
+ * (left, top, right, and bottom).
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue The start Rect
+ * @param endValue The end Rect
+ * @return A linear interpolation between the start and end values, given the
+ * <code>fraction</code> parameter.
+ */
+ @Override
+ public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
+ return new Rect(startValue.left + (int)((endValue.left - startValue.left) * fraction),
+ startValue.top + (int)((endValue.top - startValue.top) * fraction),
+ startValue.right + (int)((endValue.right - startValue.right) * fraction),
+ startValue.bottom + (int)((endValue.bottom - startValue.bottom) * fraction));
+ }
+}
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 3602fc4..c4ddf1f 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -695,6 +695,86 @@
public boolean isTitleTruncated() { return false; }
/**
+ * Set an alternate drawable to display next to the icon/logo/title
+ * when {@link #DISPLAY_HOME_AS_UP} is enabled. This can be useful if you are using
+ * this mode to display an alternate selection for up navigation, such as a sliding drawer.
+ *
+ * <p>If you pass <code>null</code> to this method, the default drawable from the theme
+ * will be used.</p>
+ *
+ * <p>If you implement alternate or intermediate behavior around Up, you should also
+ * call {@link #setHomeActionContentDescription(int) setHomeActionContentDescription()}
+ * to provide a correct description of the action for accessibility support.</p>
+ *
+ * @param indicator A drawable to use for the up indicator, or null to use the theme's default
+ *
+ * @see #setDisplayOptions(int, int)
+ * @see #setDisplayHomeAsUpEnabled(boolean)
+ * @see #setHomeActionContentDescription(int)
+ */
+ public void setHomeAsUpIndicator(Drawable indicator) { }
+
+ /**
+ * Set an alternate drawable to display next to the icon/logo/title
+ * when {@link #DISPLAY_HOME_AS_UP} is enabled. This can be useful if you are using
+ * this mode to display an alternate selection for up navigation, such as a sliding drawer.
+ *
+ * <p>If you pass <code>0</code> to this method, the default drawable from the theme
+ * will be used.</p>
+ *
+ * <p>If you implement alternate or intermediate behavior around Up, you should also
+ * call {@link #setHomeActionContentDescription(int) setHomeActionContentDescription()}
+ * to provide a correct description of the action for accessibility support.</p>
+ *
+ * @param resId Resource ID of a drawable to use for the up indicator, or null
+ * to use the theme's default
+ *
+ * @see #setDisplayOptions(int, int)
+ * @see #setDisplayHomeAsUpEnabled(boolean)
+ * @see #setHomeActionContentDescription(int)
+ */
+ public void setHomeAsUpIndicator(int resId) { }
+
+ /**
+ * Set an alternate description for the Home/Up action, when enabled.
+ *
+ * <p>This description is commonly used for accessibility/screen readers when
+ * the Home action is enabled. (See {@link #setDisplayHomeAsUpEnabled(boolean)}.)
+ * Examples of this are, "Navigate Home" or "Navigate Up" depending on the
+ * {@link #DISPLAY_HOME_AS_UP} display option. If you have changed the home-as-up
+ * indicator using {@link #setHomeAsUpIndicator(int)} to indicate more specific
+ * functionality such as a sliding drawer, you should also set this to accurately
+ * describe the action.</p>
+ *
+ * <p>Setting this to <code>null</code> will use the system default description.</p>
+ *
+ * @param description New description for the Home action when enabled
+ * @see #setHomeAsUpIndicator(int)
+ * @see #setHomeAsUpIndicator(android.graphics.drawable.Drawable)
+ */
+ public void setHomeActionContentDescription(CharSequence description) { }
+
+ /**
+ * Set an alternate description for the Home/Up action, when enabled.
+ *
+ * <p>This description is commonly used for accessibility/screen readers when
+ * the Home action is enabled. (See {@link #setDisplayHomeAsUpEnabled(boolean)}.)
+ * Examples of this are, "Navigate Home" or "Navigate Up" depending on the
+ * {@link #DISPLAY_HOME_AS_UP} display option. If you have changed the home-as-up
+ * indicator using {@link #setHomeAsUpIndicator(int)} to indicate more specific
+ * functionality such as a sliding drawer, you should also set this to accurately
+ * describe the action.</p>
+ *
+ * <p>Setting this to <code>0</code> will use the system default description.</p>
+ *
+ * @param resId Resource ID of a string to use as the new description
+ * for the Home action when enabled
+ * @see #setHomeAsUpIndicator(int)
+ * @see #setHomeAsUpIndicator(android.graphics.drawable.Drawable)
+ */
+ public void setHomeActionContentDescription(int resId) { }
+
+ /**
* Listener interface for ActionBar navigation events.
*/
public interface OnNavigationListener {
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 @@
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 @@
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 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 @@
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 @@
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 @@
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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ae0671b..68a2397 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -86,6 +86,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.renderscript.RenderScript;
+import android.security.AndroidKeyStoreProvider;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
@@ -101,6 +102,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
+import java.security.Security;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -5068,6 +5070,8 @@
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
+ Security.addProvider(new AndroidKeyStoreProvider());
+
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 459e49c..9bf8830 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1020,7 +1020,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
@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 @@
@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 @@
< 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/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 3d9b2ae..92ec3ad 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -17,12 +17,12 @@
package android.app;
-import android.app.INotificationListener;
import android.app.ITransientNotification;
+import android.service.notification.StatusBarNotification;
import android.app.Notification;
+import android.content.ComponentName;
import android.content.Intent;
-
-import com.android.internal.statusbar.StatusBarNotification;
+import android.service.notification.INotificationListener;
/** {@hide} */
interface INotificationManager
@@ -41,7 +41,9 @@
StatusBarNotification[] getActiveNotifications(String callingPkg);
StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);
- void registerListener(in INotificationListener listener, String pkg, int userid);
+ void registerListener(in INotificationListener listener, in ComponentName component, int userid);
void unregisterListener(in INotificationListener listener, int userid);
-}
+ void clearNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
+ void clearAllNotificationsFromListener(in INotificationListener token);
+}
\ No newline at end of file
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 @@
}
}
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 @@
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 @@
}
}
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 @@
}
}
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/Notification.java b/core/java/android/app/Notification.java
index ebca041..a7543a8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1603,7 +1603,7 @@
n.defaults = mDefaults;
n.flags = mFlags;
n.bigContentView = makeBigContentView();
- if (mLedOnMs != 0 && mLedOffMs != 0) {
+ if (mLedOnMs != 0 || mLedOffMs != 0) {
n.flags |= FLAG_SHOW_LIGHTS;
}
if ((mDefaults & DEFAULT_LIGHTS) != 0) {
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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 83e95ca..3c1ec90 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -262,6 +262,26 @@
public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
/**
+ * Bluetooth device type, Unknown
+ */
+ public static final int DEVICE_TYPE_UNKNOWN = 0;
+
+ /**
+ * Bluetooth device type, Classic - BR/EDR devices
+ */
+ public static final int DEVICE_TYPE_CLASSIC = 1;
+
+ /**
+ * Bluetooth device type, Low Energy - LE-only
+ */
+ public static final int DEVICE_TYPE_LE = 2;
+
+ /**
+ * Bluetooth device type, Dual Mode - BR/EDR/LE
+ */
+ public static final int DEVICE_TYPE_DUAL = 3;
+
+ /**
* Broadcast Action: This intent is used to broadcast the {@link UUID}
* wrapped as a {@link android.os.ParcelUuid} of the remote device after it
* has been fetched. This intent is sent only when the UUIDs of the remote
@@ -602,6 +622,26 @@
}
/**
+ * Get the Bluetooth device type of the remote device.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE}
+ * {@link #DEVICE_TYPE_DUAL}.
+ * {@link #DEVICE_TYPE_UNKNOWN} if it's not available
+ */
+ public int getType() {
+ if (sService == null) {
+ Log.e(TAG, "BT not enabled. Cannot get Remote Device type");
+ return DEVICE_TYPE_UNKNOWN;
+ }
+ try {
+ return sService.getRemoteType(this);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return DEVICE_TYPE_UNKNOWN;
+ }
+
+ /**
* Get the Bluetooth alias of the remote device.
* <p>Alias is the locally modified name of a remote device.
*
@@ -1139,8 +1179,8 @@
* device becomes available (true).
* @throws IllegalArgumentException if callback is null
*/
- public BluetoothGatt connectGattServer(Context context, boolean autoConnect,
- BluetoothGattCallback callback) {
+ public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+ BluetoothGattCallback callback) {
// TODO(Bluetooth) check whether platform support BLE
// Do the check here or in GattServer?
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index f9ce6ea..bffe64b 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -43,7 +43,7 @@
* with Bluetooth Smart or Smart Ready devices.
*
* <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
- * and call {@link BluetoothDevice#connectGattServer} to get a instance of this class.
+ * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
* GATT capable devices can be discovered using the Bluetooth device discovery or BLE
* scan process.
*/
@@ -66,6 +66,7 @@
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 static final int CONN_STATE_CLOSED = 4;
private List<BluetoothGattService> mServices;
@@ -135,7 +136,7 @@
}
mClientIf = clientIf;
if (status != GATT_SUCCESS) {
- mCallback.onConnectionStateChange(mDevice, GATT_FAILURE,
+ mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
BluetoothProfile.STATE_DISCONNECTED);
synchronized(mStateLock) {
mConnState = CONN_STATE_IDLE;
@@ -164,7 +165,7 @@
int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
BluetoothProfile.STATE_DISCONNECTED;
try {
- mCallback.onConnectionStateChange(mDevice, status, profileState);
+ mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -291,7 +292,7 @@
return;
}
try {
- mCallback.onServicesDiscovered(mDevice, status);
+ mCallback.onServicesDiscovered(BluetoothGatt.this, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -338,7 +339,7 @@
if (status == 0) characteristic.setValue(value);
try {
- mCallback.onCharacteristicRead(characteristic, status);
+ mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -384,7 +385,7 @@
mAuthRetry = false;
try {
- mCallback.onCharacteristicWrite(characteristic, status);
+ mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -415,7 +416,7 @@
characteristic.setValue(value);
try {
- mCallback.onCharacteristicChanged(characteristic);
+ mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -464,7 +465,7 @@
mAuthRetry = true;
try {
- mCallback.onDescriptorRead(descriptor, status);
+ mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -512,7 +513,7 @@
mAuthRetry = false;
try {
- mCallback.onDescriptorWrite(descriptor, status);
+ mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -529,7 +530,7 @@
return;
}
try {
- mCallback.onReliableWriteCompleted(mDevice, status);
+ mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -546,7 +547,7 @@
return;
}
try {
- mCallback.onReadRemoteRssi(mDevice, rssi, status);
+ mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -563,12 +564,13 @@
}
/**
- * Close the connection to the gatt service.
+ * Close this Bluetooth GATT client.
*/
- /*package*/ void close() {
+ public void close() {
if (DBG) Log.d(TAG, "close()");
unregisterApp();
+ mConnState = CONN_STATE_CLOSED;
}
/**
@@ -694,7 +696,35 @@
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
- // TBD deregister after conneciton is torn down
+ }
+
+ /**
+ * Connect back to remote device.
+ *
+ * <p>This method is used to re-connect to a remote device after the
+ * connection has been dropped. If the device is not in range, the
+ * re-connection will be triggered once the device is back in range.
+ *
+ * @return true, if the connection attempt was initiated successfully
+ */
+ public boolean connect() {
+ try {
+ mService.clientConnect(mClientIf, mDevice.getAddress(),
+ false); // autoConnect is inverse of "isDirect"
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+ }
+
+ /**
+ * Return the remote bluetooth device this GATT client targets to
+ *
+ * @return remote bluetooth device
+ */
+ public BluetoothDevice getDevice() {
+ return mDevice;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index c9e5fea..2259c1e 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -16,23 +16,22 @@
package android.bluetooth;
-import android.bluetooth.BluetoothDevice;
-
/**
* This abstract class is used to implement {@link BluetoothGatt} callbacks.
*/
public abstract class BluetoothGattCallback {
/**
- * Callback indicating when a remote device has been connected or disconnected.
+ * Callback indicating when GATT client has connected/disconnected to/from a remote
+ * GATT server.
*
- * @param device Remote device that has been connected or disconnected.
+ * @param gatt GATT client
* @param status Status of the connect or disconnect operation.
* @param newState Returns the new connection state. Can be one of
* {@link BluetoothProfile#STATE_DISCONNECTED} or
* {@link BluetoothProfile#STATE_CONNECTED}
*/
- public void onConnectionStateChange(BluetoothDevice device, int status,
+ public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
}
@@ -40,22 +39,23 @@
* 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 gatt GATT client invoked {@link BluetoothGatt#discoverServices}
* @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device
* has been explored successfully.
*/
- public void onServicesDiscovered(BluetoothDevice device, int status) {
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
}
/**
* Callback reporting the result of a characteristic read operation.
*
+ * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
* @param characteristic Characteristic that was read from the associated
* remote device.
* @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
* was completed successfully.
*/
- public void onCharacteristicRead(BluetoothGattCharacteristic characteristic,
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
int status) {
}
@@ -68,52 +68,59 @@
* value to the desired value to be written. If the values don't match,
* the application must abort the reliable write transaction.
*
+ * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
* @param characteristic Characteristic that was written to the associated
* remote device.
* @param status The result of the write operation
*/
- public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic,
- int status) {
+ public void onCharacteristicWrite(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, int status) {
}
/**
* Callback triggered as a result of a remote characteristic notification.
*
+ * @param gatt GATT client the characteristic is associated with
* @param characteristic Characteristic that has been updated as a result
* of a remote notification event.
*/
- public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) {
+ public void onCharacteristicChanged(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic) {
}
/**
* Callback reporting the result of a descriptor read operation.
*
+ * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
* @param descriptor Descriptor that was read from the associated
* 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(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
}
/**
* Callback indicating the result of a descriptor write operation.
*
+ * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
* @param descriptor Descriptor that was writte to the associated
* remote device.
* @param status The result of the write operation
*/
- public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) {
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
}
/**
* Callback invoked when a reliable write transaction has been completed.
*
- * @param device Remote device
+ * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite}
* @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write
* transaction was executed successfully
*/
- public void onReliableWriteCompleted(BluetoothDevice device, int status) {
+ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
}
/**
@@ -122,10 +129,10 @@
* This callback is triggered in response to the
* {@link BluetoothGatt#readRemoteRssi} function.
*
- * @param device Identifies the remote device
+ * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi}
* @param rssi The RSSI value for the remote device
* @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
*/
- public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) {
+ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
}
}
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index d63d97e..033f079 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -22,6 +22,10 @@
/**
* Represents a Bluetooth GATT Characteristic
+ *
+ * <p>A GATT characteristic is a basic data element used to construct a GATT service,
+ * {@link BluetoothGattService}. The characteristic contains a value as well as
+ * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}.
*/
public class BluetoothGattCharacteristic {
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index 6ba2db7..1cd6878 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -20,6 +20,10 @@
/**
* Represents a Bluetooth GATT Descriptor
+ *
+ * <p> GATT Descriptors contain additional information and attributes of a GATT
+ * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe
+ * the characteristic's features or to control certain behaviours of the characteristic.
*/
public class BluetoothGattDescriptor {
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index d1f4b82..644c619b 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -554,9 +554,10 @@
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);
}
}
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
index c3b3cfe..39a435b 100644
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -23,6 +23,9 @@
/**
* Represents a Bluetooth GATT Service
+ *
+ * <p> Gatt Service contains a collection of {@link BluetoothGattCharacteristic},
+ * as well as referenced services.
*/
public class BluetoothGattService {
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index d016c26..80806f9 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -60,6 +60,7 @@
int getBondState(in BluetoothDevice device);
String getRemoteName(in BluetoothDevice device);
+ int getRemoteType(in BluetoothDevice device);
String getRemoteAlias(in BluetoothDevice device);
boolean setRemoteAlias(in BluetoothDevice device, in String name);
int getRemoteClass(in BluetoothDevice device);
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 @@
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.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 @@
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.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 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/Intent.java b/core/java/android/content/Intent.java
index 0efd6b0..97ad7dd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -32,6 +32,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.StrictMode;
import android.util.AttributeSet;
import android.util.Log;
@@ -2578,6 +2579,14 @@
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()).
@@ -6958,6 +6967,32 @@
}
/**
+ * 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/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 @@
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 @@
* @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 @@
mDigest = source.createByteArray();
}
- static ManifestDigest fromAttributes(Attributes attributes) {
- if (attributes == null) {
+ static ManifestDigest fromInputStream(InputStream fileIs) {
+ if (fileIs == null) {
return null;
}
- String encodedDigest = null;
+ final MessageDigest md;
+ try {
+ md = MessageDigest.getInstance(DIGEST_ALGORITHM);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(DIGEST_ALGORITHM + " must be available", e);
+ }
- for (int i = 0; i < DIGEST_TYPES.length; i++) {
- final String value = attributes.getValue(DIGEST_TYPES[i]);
- if (value != null) {
- encodedDigest = value;
- break;
+ 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
}
- }
-
- if (encodedDigest == null) {
+ } 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/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 11f9be93..384aed8 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.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.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;
@@ -567,6 +565,28 @@
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;
@@ -618,7 +638,6 @@
}
} else {
Enumeration<JarEntry> entries = jarFile.entries();
- final Manifest manifest = jarFile.getManifest();
while (entries.hasMoreElements()) {
final JarEntry je = entries.nextElement();
if (je.isDirectory()) continue;
@@ -629,8 +648,8 @@
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);
@@ -1017,25 +1036,10 @@
return null;
}
} else if (tagName.equals("uses-permission")) {
- sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AndroidManifestUsesPermission);
-
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- String name = sa.getNonResourceString(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
- 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(required ? Boolean.TRUE : Boolean.FALSE);
+ if (!parseUsesPermission(pkg, res, parser, attrs, outError)) {
+ return null;
}
- XmlUtils.skipCurrentTag(parser);
-
} else if (tagName.equals("uses-configuration")) {
ConfigurationInfo cPref = new ConfigurationInfo();
sa = res.obtainAttributes(attrs,
@@ -1379,9 +1383,53 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
}
+ /*
+ * b/8528162: Ignore the <uses-permission android:required> attribute if
+ * targetSdkVersion < JELLY_BEAN_MR2. There are lots of apps in the wild
+ * which are improperly using this attribute, even though it never worked.
+ */
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ for (int i = 0; i < pkg.requestedPermissionsRequired.size(); i++) {
+ pkg.requestedPermissionsRequired.set(i, Boolean.TRUE);
+ }
+ }
+
return pkg;
}
+ private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
+ AttributeSet attrs, String[] outError)
+ throws XmlPullParserException, IOException {
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestUsesPermission);
+
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String name = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
+ boolean required = sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
+
+ sa.recycle();
+
+ if (name != null) {
+ int index = pkg.requestedPermissions.indexOf(name);
+ if (index == -1) {
+ pkg.requestedPermissions.add(name.intern());
+ pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);
+ } else {
+ if (pkg.requestedPermissionsRequired.get(index) != required) {
+ outError[0] = "conflicting <uses-permission> entries";
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ return false;
+ }
+ }
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+ return true;
+ }
+
private static String buildClassName(String pkg, CharSequence clsSeq,
String[] outError) {
if (clsSeq == null || clsSeq.length() <= 0) {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 52b238f..75f8b59 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -378,7 +378,7 @@
* @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/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.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 @@
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/Binder.java b/core/java/android/os/Binder.java
index 16b4835..e9e7551 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -152,7 +152,15 @@
* not return until the current process is exiting.
*/
public static final native void joinThreadPool();
-
+
+ /**
+ * Returns true if the specified interface is a proxy.
+ * @hide
+ */
+ public static final boolean isProxy(IInterface iface) {
+ return iface.asBinder() != iface;
+ }
+
/**
* Default constructor initializes the object.
*/
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 @@
* }
* }</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 @@
*
* @hide
*/
- public final int postSyncBarrier() {
+ public int postSyncBarrier() {
return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
}
@@ -238,7 +238,7 @@
*
* @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 @@
* <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 @@
*
* @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 @@
*
* @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 @@
}
}
- final Message next() {
+ Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
@@ -218,7 +218,7 @@
}
}
- final void quit() {
+ void quit() {
if (!mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit.");
}
@@ -232,7 +232,7 @@
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 @@
}
}
- 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 @@
}
}
- 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 @@
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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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..3267939 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -203,10 +203,15 @@
*/
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 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 @@
}
/**
+ * Detect when a {@code file://} {@link android.net.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 @@
/**
* @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 @@
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/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 @@
* {@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 @@
* {@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 66083c8..367d576 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -3796,50 +3796,11 @@
* Columns in the Data_Usage_Stat table
*/
protected interface DataUsageStatColumns {
- /** What the referenced {@link Data} was used for.
- * @see DataUsageStatColumns#USAGE_TYPE_CALL
- * @see DataUsageStatColumns#USAGE_TYPE_LONG_TEXT
- * @see DataUsageStatColumns#USAGE_TYPE_SHORT_TEXT
- */
- public static final String USAGE_TYPE = "usage_type";
-
/** The last time (in milliseconds) this {@link Data} was used. */
public static final String LAST_TIME_USED = "last_time_used";
- /** The number of times the referenced {@link Data} has been used for the purpose described
- * in {@link DataUsageStatColumns#USAGE_TYPE}.
- */
+ /** The number of times the referenced {@link Data} has been used. */
public static final String TIMES_USED = "times_used";
-
- /**
- * Integer value for USAGE_TYPE.
- * This type of usage refers to voice interaction, which includes phone calls, voice chat,
- * and video chat.
- *
- * @see DataUsageFeedback#USAGE_TYPE
- * @see DataUsageStatColumns#USAGE_TYPE
- */
- public static final int USAGE_TYPE_CALL = 0;
-
- /**
- * Integer value for USAGE_TYPE.
- * This type of usage refers to text interaction involving longer messages, which includes
- * email.
- *
- * @see DataUsageFeedback#USAGE_TYPE
- * @see DataUsageStatColumns#USAGE_TYPE
- */
- public static final int USAGE_TYPE_LONG_TEXT = 1;
-
- /**
- * Integer value for USAGE_TYPE.
- * This type of usage for text interaction involving shorter messages, which includes SMS
- * and text chat with email addresses.
- *
- * @see DataUsageFeedback#USAGE_TYPE
- * @see DataUsageStatColumns#USAGE_TYPE
- */
- public static final int USAGE_TYPE_SHORT_TEXT = 2;
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 03ee9eb..88ee414 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -655,6 +655,22 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
+ /**
+ * Activity Action: Show Notification listener settings.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ * @see android.service.notification.NotificationListenerService
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS
+ = "android.settings.NOTIFICATION_LISTENER_SETTINGS";
+
// End of Intent actions for Settings
/**
@@ -5398,6 +5414,20 @@
public static final String CERT_PIN_UPDATE_METADATA_URL = "cert_pin_metadata_url";
/**
+ * URL for intent firewall updates
+ * @hide
+ */
+ public static final String INTENT_FIREWALL_UPDATE_CONTENT_URL =
+ "intent_firewall_content_url";
+
+ /**
+ * URL for intent firewall update metadata
+ * @hide
+ */
+ public static final String INTENT_FIREWALL_UPDATE_METADATA_URL =
+ "intent_firewall_metadata_url";
+
+ /**
* SELinux enforcement status. If 0, permissive; if 1, enforcing.
* @hide
*/
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index c365643..e1cc90e 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -444,6 +444,24 @@
}
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";
@@ -470,6 +488,7 @@
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
@@ -559,4 +578,6 @@
throws RemoteException;
public int is_hardware_backed() throws RemoteException;
+
+ public int clear_uid(long uid) throws RemoteException;
}
diff --git a/core/java/android/app/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
similarity index 89%
rename from core/java/android/app/INotificationListener.aidl
rename to core/java/android/service/notification/INotificationListener.aidl
index f010a2a..425fdc1 100644
--- a/core/java/android/app/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.app;
+package android.service.notification;
-import com.android.internal.statusbar.StatusBarNotification;
+import android.service.notification.StatusBarNotification;
/** @hide */
oneway interface INotificationListener
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
new file mode 100644
index 0000000..86bab2a
--- /dev/null
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -0,0 +1,138 @@
+/*
+ * 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.service.notification;
+
+import android.annotation.SdkConstant;
+import android.app.INotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+public abstract class NotificationListenerService extends Service {
+ // TAG = "NotificationListenerService[MySubclass]"
+ private final String TAG = NotificationListenerService.class.getSimpleName()
+ + "[" + getClass().getSimpleName() + "]";
+
+ private INotificationListenerWrapper mWrapper = null;
+
+ private INotificationManager mNoMan;
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE
+ = "android.service.notification.NotificationListenerService";
+
+ /**
+ * Implement this method to learn about new notifications as they are posted by apps.
+ *
+ * @param sbn A data structure encapsulating the original {@link android.app.Notification}
+ * object as well as its identifying information (tag and id) and source
+ * (package name).
+ */
+ public abstract void onNotificationPosted(StatusBarNotification sbn);
+
+ /**
+ * Implement this method to learn when notifications are removed.
+ * <P>
+ * This might occur because the user has dismissed the notification using system UI (or another
+ * notification listener) or because the app has withdrawn the notification.
+ *
+ * @param sbn A data structure encapsulating the original {@link android.app.Notification}
+ * object as well as its identifying information (tag and id) and source
+ * (package name).
+ */
+ public abstract void onNotificationRemoved(StatusBarNotification sbn);
+
+ private final INotificationManager getNotificationInterface() {
+ if (mNoMan == null) {
+ mNoMan = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ }
+ return mNoMan;
+ }
+
+ /**
+ * Inform the notification manager about dismissal of a single notification.
+ * <p>
+ * Use this if your listener has a user interface that allows the user to dismiss individual
+ * notifications, similar to the behavior of Android's status bar and notification panel.
+ * It should be called after the user dismisses a single notification using your UI;
+ * upon being informed, the notification manager will actually remove the notification
+ * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
+ * <P>
+ * <b>Note:</b> If your listener allows the user to fire a notification's
+ * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
+ * this method at that time <i>if</i> the Notification in question has the
+ * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
+ *
+ * @param pkg Package of the notifying app.
+ * @param tag Tag of the notification as specified by the notifying app in
+ * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
+ * @param id ID of the notification as specified by the notifying app in
+ * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
+ */
+ public final void clearNotification(String pkg, String tag, int id) {
+ try {
+ getNotificationInterface().clearNotificationFromListener(mWrapper, pkg, tag, id);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
+ * Inform the notification manager about dismissal of all notifications.
+ * <p>
+ * Use this if your listener has a user interface that allows the user to dismiss all
+ * notifications, similar to the behavior of Android's status bar and notification panel.
+ * It should be called after the user invokes the "dismiss all" function of your UI;
+ * upon being informed, the notification manager will actually remove all active notifications
+ * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks.
+ *
+ * {@see #clearNotification(String, String, int)}
+ */
+ public final void clearAllNotifications() {
+ try {
+ getNotificationInterface().clearAllNotificationsFromListener(mWrapper);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (mWrapper == null) {
+ mWrapper = new INotificationListenerWrapper();
+ }
+ return mWrapper;
+ }
+
+ private class INotificationListenerWrapper extends INotificationListener.Stub {
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ NotificationListenerService.this.onNotificationPosted(sbn);
+ }
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn) {
+ NotificationListenerService.this.onNotificationRemoved(sbn);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.aidl b/core/java/android/service/notification/StatusBarNotification.aidl
similarity index 93%
rename from core/java/com/android/internal/statusbar/StatusBarNotification.aidl
rename to core/java/android/service/notification/StatusBarNotification.aidl
index bd9e89c..ba81972 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.aidl
+++ b/core/java/android/service/notification/StatusBarNotification.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.statusbar;
+package android.service.notification;
parcelable StatusBarNotification;
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
similarity index 80%
rename from core/java/com/android/internal/statusbar/StatusBarNotification.java
rename to core/java/android/service/notification/StatusBarNotification.java
index 23e87fc..ef5f8c4 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.statusbar;
+package android.service.notification;
import android.app.Notification;
import android.os.Parcel;
@@ -23,34 +23,54 @@
/**
* Class encapsulating a Notification. Sent by the NotificationManagerService to clients including
- * the IStatusBar (in System UI).
+ * the status bar and any {@link android.service.notification.NotificationListenerService}s.
*/
public class StatusBarNotification implements Parcelable {
+ /** The package of the app that posted the notification. */
public final String pkg;
- public final String basePkg;
+ /** The id supplied to {@link android.app.NotificationManager#notify}. */
public final int id;
+ /** The tag supplied to {@link android.app.NotificationManager#notify}, or null if no tag
+ * was specified. */
public final String tag;
+
+ /** The notifying app's calling uid. @hide */
public final int uid;
+ /** The notifying app's base package. @hide */
+ public final String basePkg;
+ /** @hide */
public final int initialPid;
// TODO: make this field private and move callers to an accessor that
// ensures sourceUser is applied.
+
+ /** The {@link android.app.Notification} supplied to
+ * {@link android.app.NotificationManager#notify}. */
public final Notification notification;
- public final int score;
+ /** The {@link android.os.UserHandle} for whom this notification is intended. */
public final UserHandle user;
+ /** The time (in {@link System#currentTimeMillis} time) the notification was posted,
+ * which may be different than {@link android.app.Notification#when}.
+ */
public final long postTime;
- /** This is temporarily needed for the JB MR1 PDK. */
+ /** @hide */
+ public final int score;
+
+ /** This is temporarily needed for the JB MR1 PDK.
+ * @hide */
@Deprecated
public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score,
Notification notification) {
this(pkg, id, tag, uid, initialPid, score, notification, UserHandle.OWNER);
}
+ /** @hide */
public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score,
Notification notification, UserHandle user) {
this(pkg, null, id, tag, uid, initialPid, score, notification, user);
}
+ /** @hide */
public StatusBarNotification(String pkg, String basePkg, int id, String tag, int uid,
int initialPid, int score, Notification notification, UserHandle user) {
this(pkg, basePkg, id, tag, uid, initialPid, score, notification, user,
@@ -147,10 +167,17 @@
this.score, this.notification);
}
+ /** Convenience method to check the notification's flags for
+ * {@link Notification#FLAG_ONGOING_EVENT}.
+ */
public boolean isOngoing() {
return (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
}
+ /** Convenience method to check the notification's flags for
+ * either {@link Notification#FLAG_ONGOING_EVENT} or
+ * {@link Notification#FLAG_NO_CLEAR}.
+ */
public boolean isClearable() {
return ((notification.flags & Notification.FLAG_ONGOING_EVENT) == 0)
&& ((notification.flags & Notification.FLAG_NO_CLEAR) == 0);
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.TimeZone;
import java.text.SimpleDateFormat;
+import libcore.icu.ICU;
import libcore.icu.LocaleData;
/**
@@ -43,6 +44,9 @@
* 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 @@
}
/**
+ * 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/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 9860588..2bc1c6a 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -16,6 +16,7 @@
package android.text.util;
+import android.telephony.PhoneNumberUtils;
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.text.style.URLSpan;
@@ -32,9 +33,14 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import com.android.i18n.phonenumbers.PhoneNumberMatch;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
+
/**
* Linkify take a piece of text and a regular expression and turns all of the
* regex matches in the text into clickable links. This is particularly
@@ -221,9 +227,7 @@
}
if ((mask & PHONE_NUMBERS) != 0) {
- gatherLinks(links, text, Patterns.PHONE,
- new String[] { "tel:" },
- sPhoneNumberMatchFilter, sPhoneNumberTransformFilter);
+ gatherTelLinks(links, text);
}
if ((mask & MAP_ADDRESSES) != 0) {
@@ -443,6 +447,19 @@
}
}
+ private static final void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) {
+ PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
+ Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
+ Locale.getDefault().getCountry(), Leniency.POSSIBLE, Long.MAX_VALUE);
+ for (PhoneNumberMatch match : matches) {
+ LinkSpec spec = new LinkSpec();
+ spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
+ spec.start = match.start();
+ spec.end = match.end();
+ links.add(spec);
+ }
+ }
+
private static final void gatherMapLinks(ArrayList<LinkSpec> links, Spannable s) {
String string = s.toString();
String address;
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 7918823..8055077 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -451,10 +451,8 @@
* @param attachInfo AttachInfo tied to the specified view.
* @param callbacks Callbacks invoked when drawing happens.
* @param dirty The dirty rectangle to update, can be null.
- *
- * @return true if the dirty rect was ignored, false otherwise
*/
- abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
+ abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty);
/**
@@ -992,11 +990,7 @@
mCanvas = createCanvas();
mCanvas.setName(mName);
}
- if (mCanvas != null) {
- setEnabled(true);
- } else {
- Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
- }
+ setEnabled(true);
}
return mCanvas != null;
@@ -1340,7 +1334,7 @@
}
@Override
- boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
+ void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty) {
if (canDraw()) {
if (!hasDirtyRegions()) {
@@ -1401,11 +1395,8 @@
}
attachInfo.mIgnoreDirtyState = false;
- return dirty == null;
}
}
-
- return false;
}
private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
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 @@
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/InputChannel.java b/core/java/android/view/InputChannel.java
index a797176..40ee1ad 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -54,6 +54,7 @@
private native void nativeTransferTo(InputChannel other);
private native void nativeReadFromParcel(Parcel parcel);
private native void nativeWriteToParcel(Parcel parcel);
+ private native void nativeDup(InputChannel target);
private native String nativeGetName();
@@ -64,7 +65,7 @@
*/
public InputChannel() {
}
-
+
@Override
protected void finalize() throws Throwable {
try {
@@ -125,6 +126,15 @@
nativeTransferTo(outParameter);
}
+ /**
+ * Duplicates the input channel.
+ */
+ public InputChannel dup() {
+ InputChannel target = new InputChannel();
+ nativeDup(target);
+ return target;
+ }
+
@Override
public int describeContents() {
return Parcelable.CONTENTS_FILE_DESCRIPTOR;
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index dd523d2..2595ee5 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -371,8 +371,8 @@
if (axis < 0) {
break;
}
- addMotionRange(axis, in.readInt(),
- in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat());
+ addMotionRange(axis, in.readInt(), in.readFloat(), in.readFloat(), in.readFloat(),
+ in.readFloat(), in.readFloat());
}
}
@@ -584,8 +584,8 @@
// Called from native code.
private void addMotionRange(int axis, int source,
- float min, float max, float flat, float fuzz) {
- mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz));
+ float min, float max, float flat, float fuzz, float resolution) {
+ mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz, resolution));
}
/**
@@ -625,14 +625,17 @@
private float mMax;
private float mFlat;
private float mFuzz;
+ private float mResolution;
- private MotionRange(int axis, int source, float min, float max, float flat, float fuzz) {
+ private MotionRange(int axis, int source, float min, float max, float flat, float fuzz,
+ float resolution) {
mAxis = axis;
mSource = source;
mMin = min;
mMax = max;
mFlat = flat;
mFuzz = fuzz;
+ mResolution = resolution;
}
/**
@@ -711,6 +714,14 @@
public float getFuzz() {
return mFuzz;
}
+
+ /**
+ * Gets the resolution for input device measurements with respect to this axis.
+ * @return The resolution in units per millimeter, or units per radian for rotational axes.
+ */
+ public float getResolution() {
+ return mResolution;
+ }
}
@Override
@@ -734,6 +745,7 @@
out.writeFloat(range.mMax);
out.writeFloat(range.mFlat);
out.writeFloat(range.mFuzz);
+ out.writeFloat(range.mResolution);
}
out.writeInt(-1);
}
@@ -788,6 +800,7 @@
description.append(" max=").append(range.mMax);
description.append(" flat=").append(range.mFlat);
description.append(" fuzz=").append(range.mFuzz);
+ description.append(" resolution=").append(range.mResolution);
description.append("\n");
}
return description.toString();
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.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 @@
// 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 @@
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.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 @@
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 @@
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/Overlay.java b/core/java/android/view/Overlay.java
deleted file mode 100644
index 6630752..0000000
--- a/core/java/android/view/Overlay.java
+++ /dev/null
@@ -1,81 +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.view;
-
-import android.graphics.drawable.Drawable;
-
-/**
- * An overlay is an extra layer that sits on top of a View (the "host view")
- * which is drawn after all other content in that view (including children,
- * if the view is a ViewGroup). Interaction with the overlay layer is done in
- * terms of adding/removing views and drawables.
- *
- * @see android.view.View#getOverlay()
- */
-public interface Overlay {
-
- /**
- * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
- * the host view. Any drawable added to the overlay should be removed when it is no longer
- * needed or no longer visible.
- *
- * @param drawable The Drawable to be added to the overlay. This drawable will be
- * drawn when the view redraws its overlay.
- * @see #remove(android.graphics.drawable.Drawable)
- * @see #add(View)
- */
- void add(Drawable drawable);
-
- /**
- * Removes the specified Drawable from the overlay.
- *
- * @param drawable The Drawable to be removed from the overlay.
- * @see #add(android.graphics.drawable.Drawable)
- */
- void remove(Drawable drawable);
-
- /**
- * Adds a View to the overlay. The bounds of the added view should be
- * relative to the host view. Any view added to the overlay should be
- * removed when it is no longer needed or no longer visible.
- *
- * <p>If the view has a parent, the view will be removed from that parent
- * before being added to the overlay. Also, the view will be repositioned
- * such that it is in the same relative location inside the activity. For
- * example, if the view's current parent lies 100 pixels to the right
- * and 200 pixels down from the origin of the overlay's
- * host view, then the view will be offset by (100, 200).</p>
- *
- * @param view The View to be added to the overlay. The added view will be
- * drawn when the overlay is drawn.
- * @see #remove(View)
- * @see #add(android.graphics.drawable.Drawable)
- */
- void add(View view);
-
- /**
- * Removes the specified View from the overlay.
- *
- * @param view The View to be removed from the overlay.
- * @see #add(View)
- */
- void remove(View view);
-
- /**
- * Removes all views and drawables from the overlay.
- */
- void clear();
-}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a5b3c8f..7c82f7e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6889,7 +6889,7 @@
/**
* 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 @@
* <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>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>
+ * <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>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>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.</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
@@ -12098,7 +12107,7 @@
//System.out.println("Attached! " + this);
mAttachInfo = info;
if (mOverlay != null) {
- mOverlay.dispatchAttachedToWindow(info, visibility);
+ mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
}
mWindowAttachCount++;
// We will need to evaluate the drawable state at least once.
@@ -12169,7 +12178,7 @@
mAttachInfo = null;
if (mOverlay != null) {
- mOverlay.dispatchDetachedFromWindow();
+ mOverlay.getOverlayView().dispatchDetachedFromWindow();
}
}
@@ -12365,13 +12374,11 @@
* </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 @@
* <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
@@ -12827,7 +12831,7 @@
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.draw(canvas);
+ mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
@@ -13143,7 +13147,7 @@
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.draw(canvas);
+ mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
@@ -13901,7 +13905,7 @@
onDrawScrollBars(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.dispatchDraw(canvas);
+ mOverlay.getOverlayView().dispatchDraw(canvas);
}
// we're done...
@@ -14045,34 +14049,28 @@
onDrawScrollBars(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.dispatchDraw(canvas);
+ mOverlay.getOverlayView().dispatchDraw(canvas);
}
}
/**
- * Called by the addToOverlay() methods to create, attach, and size the overlay as necessary
+ * Returns the overlay for this view, creating it if it does not yet exist.
+ * Adding drawables to the overlay will cause them to be displayed whenever
+ * the view itself is redrawn. Objects in the overlay should be actively
+ * managed: remove them when they should not be displayed anymore. The
+ * overlay will always have the same size as its host view.
+ *
+ * <p>Note: Overlays do not currently work correctly with {@link
+ * SurfaceView} or {@link TextureView}; contents in overlays for these
+ * types of views may not display correctly.</p>
+ *
+ * @return The ViewOverlay object for this view.
+ * @see ViewOverlay
*/
- private void setupOverlay() {
+ public ViewOverlay getOverlay() {
if (mOverlay == null) {
mOverlay = new ViewOverlay(mContext, this);
- mOverlay.mAttachInfo = mAttachInfo;
- mOverlay.setRight(mRight);
- mOverlay.setBottom(mBottom);
}
- }
-
- /**
- * Returns the overlay for this view, creating it if it does not yet exist. Adding drawables
- * and/or views to the overlay will cause them to be displayed whenever the view itself is
- * redrawn. Objects in the overlay should be actively managed: remove them when they should
- * not be displayed anymore and invalidate this view appropriately when overlay drawables
- * change. The overlay will always have the same size as its host view.
- *
- * @return The Overlay object for this view.
- * @see Overlay
- */
- public Overlay getOverlay() {
- setupOverlay();
return mOverlay;
}
@@ -14356,8 +14354,8 @@
private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
if (mOverlay != null) {
- mOverlay.setRight(mRight);
- mOverlay.setBottom(mBottom);
+ mOverlay.getOverlayView().setRight(newWidth);
+ mOverlay.getOverlayView().setBottom(newHeight);
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 725502d..311d1d0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -406,11 +406,17 @@
private View[] mChildren;
// Number of valid children in the mChildren array, the rest should be null or not
// considered as children
-
- private boolean mLayoutSuppressed = false;
-
private int mChildrenCount;
+ // Whether layout calls are currently being suppressed, controlled by calls to
+ // suppressLayout()
+ boolean mSuppressLayout = false;
+
+ // Whether any layout calls have actually been suppressed while mSuppressLayout
+ // has been true. This tracks whether we need to issue a requestLayout() when
+ // layout is later re-enabled.
+ private boolean mLayoutCalledWhileSuppressed = false;
+
private static final int ARRAY_INITIAL_CAPACITY = 12;
private static final int ARRAY_CAPACITY_INCREMENT = 12;
@@ -1870,34 +1876,10 @@
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
- final float x = ev.getX(actionIndex);
- final float y = ev.getY(actionIndex);
-
- if (mOverlay != null) {
- ViewOverlay overlay = (ViewOverlay) mOverlay;
- // Check to see whether the overlay can handle the event
- final View child = mOverlay;
- if (canViewReceivePointerEvents(child) &&
- isTransformedTouchPointInView(x, y, child, null)) {
- newTouchTarget = getTouchTarget(child);
- if (newTouchTarget != null) {
- newTouchTarget.pointerIdBits |= idBitsToAssign;
- } else {
- resetCancelNextUpFlag(child);
- if (dispatchTransformedTouchEvent(ev, false, child,
- idBitsToAssign)) {
- mLastTouchDownTime = ev.getDownTime();
- mLastTouchDownX = ev.getX();
- mLastTouchDownY = ev.getY();
- newTouchTarget = addTouchTarget(child, idBitsToAssign);
- alreadyDispatchedToNewTouchTarget = true;
- }
- }
- }
- }
-
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
+ final float x = ev.getX(actionIndex);
+ final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final View[] children = mChildren;
@@ -2564,7 +2546,7 @@
exitHoverTargets();
// In case view is detached while transition is running
- mLayoutSuppressed = false;
+ mLayoutCalledWhileSuppressed = false;
// Tear down our drag tracking
mDragNotifiedChildren = null;
@@ -2985,6 +2967,30 @@
}
/**
+ * Returns the ViewGroupOverlay for this view group, creating it if it does
+ * not yet exist. In addition to {@link ViewOverlay}'s support for drawables,
+ * {@link ViewGroupOverlay} allows views to be added to the overlay. These
+ * views, like overlay drawables, are visual-only; they do not receive input
+ * events and should not be used as anything other than a temporary
+ * representation of a view in a parent container, such as might be used
+ * by an animation effect.
+ *
+ * <p>Note: Overlays do not currently work correctly with {@link
+ * SurfaceView} or {@link TextureView}; contents in overlays for these
+ * types of views may not display correctly.</p>
+ *
+ * @return The ViewGroupOverlay object for this view.
+ * @see ViewGroupOverlay
+ */
+ @Override
+ public ViewGroupOverlay getOverlay() {
+ if (mOverlay == null) {
+ mOverlay = new ViewGroupOverlay(mContext, this);
+ }
+ return (ViewGroupOverlay) mOverlay;
+ }
+
+ /**
* Returns the index of the child to draw for this iteration. Override this
* if you want to change the drawing order of children. By default, it
* returns i.
@@ -3049,11 +3055,12 @@
}
}
if (mOverlay != null) {
- mOverlay.mRecreateDisplayList = (mOverlay.mPrivateFlags & PFLAG_INVALIDATED)
+ View overlayView = mOverlay.getOverlayView();
+ overlayView.mRecreateDisplayList = (overlayView.mPrivateFlags & PFLAG_INVALIDATED)
== PFLAG_INVALIDATED;
- mOverlay.mPrivateFlags &= ~PFLAG_INVALIDATED;
- mOverlay.getDisplayList();
- mOverlay.mRecreateDisplayList = false;
+ overlayView.mPrivateFlags &= ~PFLAG_INVALIDATED;
+ overlayView.getDisplayList();
+ overlayView.mRecreateDisplayList = false;
}
}
@@ -4525,7 +4532,7 @@
super.layout(l, t, r, b);
} else {
// record the fact that we noop'd it; request layout when transition finishes
- mLayoutSuppressed = true;
+ mLayoutCalledWhileSuppressed = true;
}
}
@@ -5201,9 +5208,9 @@
@Override
public void endTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) {
- if (mLayoutSuppressed && !transition.isChangingLayout()) {
+ if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) {
requestLayout();
- mLayoutSuppressed = false;
+ mLayoutCalledWhileSuppressed = false;
}
if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
endViewTransition(view);
@@ -5212,6 +5219,24 @@
};
/**
+ * Tells this ViewGroup to suppress all layout() calls until layout
+ * suppression is disabled with a later call to suppressLayout(false).
+ * When layout suppression is disabled, a requestLayout() call is sent
+ * if layout() was attempted while layout was being suppressed.
+ *
+ * @hide
+ */
+ public void suppressLayout(boolean suppress) {
+ mSuppressLayout = suppress;
+ if (!suppress) {
+ if (mLayoutCalledWhileSuppressed) {
+ requestLayout();
+ mLayoutCalledWhileSuppressed = false;
+ }
+ }
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
diff --git a/core/java/android/view/ViewGroupOverlay.java b/core/java/android/view/ViewGroupOverlay.java
new file mode 100644
index 0000000..c1b24f2
--- /dev/null
+++ b/core/java/android/view/ViewGroupOverlay.java
@@ -0,0 +1,75 @@
+/*
+ * 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.view;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A group overlay is an extra layer that sits on top of a ViewGroup
+ * (the "host view") which is drawn after all other content in that view
+ * (including the view group's children). Interaction with the overlay
+ * layer is done by adding and removing views and drawables.
+ *
+ * <p>ViewGroupOverlay is a subclass of {@link ViewOverlay}, adding the ability to
+ * manage views for overlays on ViewGroups, in addition to the drawable
+ * support in ViewOverlay.</p>
+ *
+ * @see ViewGroup#getOverlay()
+ */
+public class ViewGroupOverlay extends ViewOverlay {
+
+ ViewGroupOverlay(Context context, View hostView) {
+ super(context, hostView);
+ }
+
+ /**
+ * Adds a View to the overlay. The bounds of the added view should be
+ * relative to the host view. Any view added to the overlay should be
+ * removed when it is no longer needed or no longer visible.
+ *
+ * <p>Views in the overlay are visual-only; they do not receive input
+ * events and do not participate in focus traversal. Overlay views
+ * are intended to be transient, such as might be needed by a temporary
+ * animation effect.</p>
+ *
+ * <p>If the view has a parent, the view will be removed from that parent
+ * before being added to the overlay. Also, the view will be repositioned
+ * such that it is in the same relative location inside the activity. For
+ * example, if the view's current parent lies 100 pixels to the right
+ * and 200 pixels down from the origin of the overlay's
+ * host view, then the view will be offset by (100, 200).</p>
+ *
+ * @param view The View to be added to the overlay. The added view will be
+ * drawn when the overlay is drawn.
+ * @see #remove(View)
+ * @see ViewOverlay#add(Drawable)
+ */
+ public void add(View view) {
+ mOverlayViewGroup.add(view);
+ }
+
+ /**
+ * Removes the specified View from the overlay.
+ *
+ * @param view The View to be removed from the overlay.
+ * @see #add(View)
+ * @see ViewOverlay#remove(Drawable)
+ */
+ public void remove(View view) {
+ mOverlayViewGroup.remove(view);
+ }
+}
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 8b18d53..78e2597 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -23,215 +23,286 @@
import java.util.ArrayList;
/**
- * ViewOverlay is a container that View uses to host all objects (views and
- * drawables) that are added to its "overlay", gotten through
- * {@link View#getOverlay()}. Views and drawables are added to the overlay
- * via the add/remove methods in this class. These views and drawables are
- * drawn whenever the view itself is drawn; first the view draws its own
- * content (and children, if it is a ViewGroup), then it draws its overlay
- * (if it has one).
+ * An overlay is an extra layer that sits on top of a View (the "host view")
+ * which is drawn after all other content in that view (including children,
+ * if the view is a ViewGroup). Interaction with the overlay layer is done
+ * by adding and removing drawables.
*
- * Besides managing and drawing the list of drawables, this class serves
- * two purposes:
- * (1) it noops layout calls because children are absolutely positioned and
- * (2) it forwards all invalidation calls to its host view. The invalidation
- * redirect is necessary because the overlay is not a child of the host view
- * and invalidation cannot therefore follow the normal path up through the
- * parent hierarchy.
+ * <p>An overlay requested from a ViewGroup is of type {@link ViewGroupOverlay},
+ * which also supports adding and removing views.</p>
*
- * @hide
+ * @see View#getOverlay() View.getOverlay()
+ * @see ViewGroup#getOverlay() ViewGroup.getOverlay()
+ * @see ViewGroupOverlay
*/
-class ViewOverlay extends ViewGroup implements Overlay {
+public class ViewOverlay {
/**
- * The View for which this is an overlay. Invalidations of the overlay are redirected to
- * this host view.
+ * The actual container for the drawables (and views, if it's a ViewGroupOverlay).
+ * All of the management and rendering details for the overlay are handled in
+ * OverlayViewGroup.
*/
- View mHostView;
+ OverlayViewGroup mOverlayViewGroup;
- /**
- * The set of drawables to draw when the overlay is rendered.
- */
- ArrayList<Drawable> mDrawables = null;
-
- ViewOverlay(Context context, View host) {
- super(context);
- mHostView = host;
- mParent = mHostView.getParent();
+ ViewOverlay(Context context, View hostView) {
+ mOverlayViewGroup = new OverlayViewGroup(context, hostView);
}
- @Override
+ /**
+ * Used internally by View and ViewGroup to handle drawing and invalidation
+ * of the overlay
+ * @return
+ */
+ ViewGroup getOverlayView() {
+ return mOverlayViewGroup;
+ }
+
+ /**
+ * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
+ * the host view. Any drawable added to the overlay should be removed when it is no longer
+ * needed or no longer visible.
+ *
+ * @param drawable The Drawable to be added to the overlay. This drawable will be
+ * drawn when the view redraws its overlay.
+ * @see #remove(Drawable)
+ */
public void add(Drawable drawable) {
- if (mDrawables == null) {
- mDrawables = new ArrayList<Drawable>();
- }
- if (!mDrawables.contains(drawable)) {
- // Make each drawable unique in the overlay; can't add it more than once
- mDrawables.add(drawable);
- invalidate(drawable.getBounds());
- drawable.setCallback(this);
- }
+ mOverlayViewGroup.add(drawable);
}
- @Override
+ /**
+ * Removes the specified Drawable from the overlay.
+ *
+ * @param drawable The Drawable to be removed from the overlay.
+ * @see #add(Drawable)
+ */
public void remove(Drawable drawable) {
- if (mDrawables != null) {
- mDrawables.remove(drawable);
- invalidate(drawable.getBounds());
- drawable.setCallback(null);
- }
+ mOverlayViewGroup.remove(drawable);
}
- @Override
- public void invalidateDrawable(Drawable drawable) {
- invalidate(drawable.getBounds());
- }
-
- @Override
- public void add(View child) {
- int deltaX = 0;
- int deltaY = 0;
- if (child.getParent() instanceof ViewGroup) {
- ViewGroup parent = (ViewGroup) child.getParent();
- if (parent != mHostView) {
- // Moving to different container; figure out how to position child such that
- // it is in the same location on the screen
- int[] parentLocation = new int[2];
- int[] hostViewLocation = new int[2];
- parent.getLocationOnScreen(parentLocation);
- mHostView.getLocationOnScreen(hostViewLocation);
- child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]);
- child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]);
- }
- parent.removeView(child);
- }
- super.addView(child);
- }
-
- @Override
- public void remove(View view) {
- super.removeView(view);
- }
-
- @Override
+ /**
+ * Removes all content from the overlay.
+ */
public void clear() {
- removeAllViews();
- mDrawables.clear();
+ mOverlayViewGroup.clear();
}
boolean isEmpty() {
- if (getChildCount() == 0 && (mDrawables == null || mDrawables.size() == 0)) {
- return true;
- }
- return false;
+ return mOverlayViewGroup.isEmpty();
}
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
- for (int i = 0; i < numDrawables; ++i) {
- mDrawables.get(i).draw(canvas);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- // Noop: children are positioned absolutely
- }
-
- /*
- The following invalidation overrides exist for the purpose of redirecting invalidation to
- the host view. The overlay is not parented to the host view (since a View cannot be a parent),
- so the invalidation cannot proceed through the normal parent hierarchy.
- There is a built-in assumption that the overlay exactly covers the host view, therefore
- the invalidation rectangles received do not need to be adjusted when forwarded to
- the host view.
+ /**
+ * OverlayViewGroup is a container that View and ViewGroup use to host
+ * drawables and views added to their overlays ({@link ViewOverlay} and
+ * {@link ViewGroupOverlay}, respectively). Drawables are added to the overlay
+ * via the add/remove methods in ViewOverlay, Views are added/removed via
+ * ViewGroupOverlay. These drawable and view objects are
+ * drawn whenever the view itself is drawn; first the view draws its own
+ * content (and children, if it is a ViewGroup), then it draws its overlay
+ * (if it has one).
+ *
+ * <p>Besides managing and drawing the list of drawables, this class serves
+ * two purposes:
+ * (1) it noops layout calls because children are absolutely positioned and
+ * (2) it forwards all invalidation calls to its host view. The invalidation
+ * redirect is necessary because the overlay is not a child of the host view
+ * and invalidation cannot therefore follow the normal path up through the
+ * parent hierarchy.</p>
+ *
+ * @see View#getOverlay()
+ * @see ViewGroup#getOverlay()
*/
+ static class OverlayViewGroup extends ViewGroup {
- @Override
- public void invalidate(Rect dirty) {
- super.invalidate(dirty);
- if (mHostView != null) {
- mHostView.invalidate(dirty);
+ /**
+ * The View for which this is an overlay. Invalidations of the overlay are redirected to
+ * this host view.
+ */
+ View mHostView;
+
+ /**
+ * The set of drawables to draw when the overlay is rendered.
+ */
+ ArrayList<Drawable> mDrawables = null;
+
+ OverlayViewGroup(Context context, View hostView) {
+ super(context);
+ mHostView = hostView;
+ mAttachInfo = mHostView.mAttachInfo;
+ mRight = hostView.getWidth();
+ mBottom = hostView.getHeight();
}
- }
- @Override
- public void invalidate(int l, int t, int r, int b) {
- super.invalidate(l, t, r, b);
- if (mHostView != null) {
- mHostView.invalidate(l, t, r, b);
- }
- }
+ public void add(Drawable drawable) {
+ if (mDrawables == null) {
- @Override
- public void invalidate() {
- super.invalidate();
- if (mHostView != null) {
- mHostView.invalidate();
- }
- }
-
- @Override
- void invalidate(boolean invalidateCache) {
- super.invalidate(invalidateCache);
- if (mHostView != null) {
- mHostView.invalidate(invalidateCache);
- }
- }
-
- @Override
- void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
- super.invalidateViewProperty(invalidateParent, forceRedraw);
- if (mHostView != null) {
- mHostView.invalidateViewProperty(invalidateParent, forceRedraw);
- }
- }
-
- @Override
- protected void invalidateParentCaches() {
- super.invalidateParentCaches();
- if (mHostView != null) {
- mHostView.invalidateParentCaches();
- }
- }
-
- @Override
- protected void invalidateParentIfNeeded() {
- super.invalidateParentIfNeeded();
- if (mHostView != null) {
- mHostView.invalidateParentIfNeeded();
- }
- }
-
- public void invalidateChildFast(View child, final Rect dirty) {
- if (mHostView != null) {
- // Note: This is not a "fast" invalidation. Would be nice to instead invalidate using DL
- // properties and a dirty rect instead of causing a real invalidation of the host view
- int left = child.mLeft;
- int top = child.mTop;
- if (!child.getMatrix().isIdentity()) {
- child.transformRect(dirty);
+ mDrawables = new ArrayList<Drawable>();
}
- dirty.offset(left, top);
- mHostView.invalidate(dirty);
- }
- }
-
- @Override
- public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
- if (mHostView != null) {
- dirty.offset(location[0], location[1]);
- if (mHostView instanceof ViewGroup) {
- location[0] = 0;
- location[1] = 0;
- super.invalidateChildInParent(location, dirty);
- return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
- } else {
- invalidate(dirty);
+ if (!mDrawables.contains(drawable)) {
+ // Make each drawable unique in the overlay; can't add it more than once
+ mDrawables.add(drawable);
+ invalidate(drawable.getBounds());
+ drawable.setCallback(this);
}
}
- return null;
+
+ public void remove(Drawable drawable) {
+ if (mDrawables != null) {
+ mDrawables.remove(drawable);
+ invalidate(drawable.getBounds());
+ drawable.setCallback(null);
+ }
+ }
+
+ public void add(View child) {
+ if (child.getParent() instanceof ViewGroup) {
+ ViewGroup parent = (ViewGroup) child.getParent();
+ if (parent != mHostView) {
+ // Moving to different container; figure out how to position child such that
+ // it is in the same location on the screen
+ int[] parentLocation = new int[2];
+ int[] hostViewLocation = new int[2];
+ parent.getLocationOnScreen(parentLocation);
+ mHostView.getLocationOnScreen(hostViewLocation);
+ child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]);
+ child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]);
+ }
+ parent.removeView(child);
+ }
+ super.addView(child);
+ }
+
+ public void remove(View view) {
+ super.removeView(view);
+ }
+
+ public void clear() {
+ removeAllViews();
+ mDrawables.clear();
+ }
+
+ boolean isEmpty() {
+ if (getChildCount() == 0 &&
+ (mDrawables == null || mDrawables.size() == 0)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void invalidateDrawable(Drawable drawable) {
+ invalidate(drawable.getBounds());
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
+ for (int i = 0; i < numDrawables; ++i) {
+ mDrawables.get(i).draw(canvas);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // Noop: children are positioned absolutely
+ }
+
+ /*
+ The following invalidation overrides exist for the purpose of redirecting invalidation to
+ the host view. The overlay is not parented to the host view (since a View cannot be a
+ parent), so the invalidation cannot proceed through the normal parent hierarchy.
+ There is a built-in assumption that the overlay exactly covers the host view, therefore
+ the invalidation rectangles received do not need to be adjusted when forwarded to
+ the host view.
+ */
+
+ @Override
+ public void invalidate(Rect dirty) {
+ super.invalidate(dirty);
+ if (mHostView != null) {
+ mHostView.invalidate(dirty);
+ }
+ }
+
+ @Override
+ public void invalidate(int l, int t, int r, int b) {
+ super.invalidate(l, t, r, b);
+ if (mHostView != null) {
+ mHostView.invalidate(l, t, r, b);
+ }
+ }
+
+ @Override
+ public void invalidate() {
+ super.invalidate();
+ if (mHostView != null) {
+ mHostView.invalidate();
+ }
+ }
+
+ @Override
+ void invalidate(boolean invalidateCache) {
+ super.invalidate(invalidateCache);
+ if (mHostView != null) {
+ mHostView.invalidate(invalidateCache);
+ }
+ }
+
+ @Override
+ void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
+ super.invalidateViewProperty(invalidateParent, forceRedraw);
+ if (mHostView != null) {
+ mHostView.invalidateViewProperty(invalidateParent, forceRedraw);
+ }
+ }
+
+ @Override
+ protected void invalidateParentCaches() {
+ super.invalidateParentCaches();
+ if (mHostView != null) {
+ mHostView.invalidateParentCaches();
+ }
+ }
+
+ @Override
+ protected void invalidateParentIfNeeded() {
+ super.invalidateParentIfNeeded();
+ if (mHostView != null) {
+ mHostView.invalidateParentIfNeeded();
+ }
+ }
+
+ public void invalidateChildFast(View child, final Rect dirty) {
+ if (mHostView != null) {
+ // Note: This is not a "fast" invalidation. Would be nice to instead invalidate
+ // using DisplayList properties and a dirty rect instead of causing a real
+ // invalidation of the host view
+ int left = child.mLeft;
+ int top = child.mTop;
+ if (!child.getMatrix().isIdentity()) {
+ child.transformRect(dirty);
+ }
+ dirty.offset(left, top);
+ mHostView.invalidate(dirty);
+ }
+ }
+
+ @Override
+ public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
+ if (mHostView != null) {
+ dirty.offset(location[0], location[1]);
+ if (mHostView instanceof ViewGroup) {
+ location[0] = 0;
+ location[1] = 0;
+ super.invalidateChildInParent(location, dirty);
+ return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
+ } else {
+ invalidate(dirty);
+ }
+ }
+ return null;
+ }
}
+
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7790f92..efa8a9e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -197,7 +197,6 @@
int mHeight;
Rect mDirty;
final Rect mCurrentDirty = new Rect();
- final Rect mPreviousDirty = new Rect();
boolean mIsAnimating;
CompatibilityInfo.Translator mTranslator;
@@ -767,10 +766,11 @@
final boolean translucent = attrs.format != PixelFormat.OPAQUE;
mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);
- mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
- mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested
- = mAttachInfo.mHardwareRenderer != null;
-
+ if (mAttachInfo.mHardwareRenderer != null) {
+ mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
+ mAttachInfo.mHardwareAccelerated =
+ mAttachInfo.mHardwareAccelerationRequested = true;
+ }
} else if (fakeHwAccelerated) {
// The window had wanted to use hardware acceleration, but this
// is not allowed in its process. By setting this flag, it can
@@ -1425,6 +1425,8 @@
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()
@@ -2385,14 +2387,10 @@
mResizeAlpha = resizeAlpha;
mCurrentDirty.set(dirty);
- mCurrentDirty.union(mPreviousDirty);
- mPreviousDirty.set(dirty);
dirty.setEmpty();
- if (attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
- animating ? null : mCurrentDirty)) {
- mPreviousDirty.set(0, 0, mWidth, mHeight);
- }
+ attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
+ animating ? null : mCurrentDirty);
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
@@ -2447,6 +2445,8 @@
canvas = mSurface.lockCanvas(dirty);
+ // The dirty rectangle can be modified by Surface.lockCanvas()
+ //noinspection ConstantConditions
if (left != dirty.left || top != dirty.top || right != dirty.right ||
bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
@@ -3097,8 +3097,7 @@
boolean inTouchMode = msg.arg2 != 0;
ensureTouchModeLocally(inTouchMode);
- if (mAttachInfo.mHardwareRenderer != null &&
- mSurface != null && mSurface.isValid()) {
+ if (mAttachInfo.mHardwareRenderer != null && mSurface.isValid()){
mFullRedrawNeeded = true;
try {
mAttachInfo.mHardwareRenderer.initializeIfNeeded(
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4207832..855b6d4 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -325,7 +325,8 @@
PendingEvent mPendingEventPool;
int mPendingEventPoolSize;
- PendingEvent mFirstPendingEvent;
+ PendingEvent mPendingEventHead;
+ PendingEvent mPendingEventTail;
// -----------------------------------------------------------
@@ -366,17 +367,14 @@
if (mBindSequence < 0 || mBindSequence != res.sequence) {
Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
+ ", given seq=" + res.sequence);
- if (res.channel != null) {
+ if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
return;
}
-
+
+ setInputChannelLocked(res.channel);
mCurMethod = res.method;
- if (mCurChannel != null) {
- mCurChannel.dispose();
- }
- mCurChannel = res.channel;
mCurId = res.id;
mBindSequence = res.sequence;
}
@@ -718,19 +716,26 @@
*/
void clearBindingLocked() {
clearConnectionLocked();
+ setInputChannelLocked(null);
mBindSequence = -1;
mCurId = null;
mCurMethod = null;
- if (mCurSender != null) {
- mCurSender.dispose();
- mCurSender = null;
- }
- if (mCurChannel != null) {
- mCurChannel.dispose();
- mCurChannel = null;
+ }
+
+ void setInputChannelLocked(InputChannel channel) {
+ if (mCurChannel != channel) {
+ if (mCurSender != null) {
+ flushPendingEventsLocked();
+ mCurSender.dispose();
+ mCurSender = null;
+ }
+ if (mCurChannel != null) {
+ mCurChannel.dispose();
+ }
+ mCurChannel = channel;
}
}
-
+
/**
* Reset all of the state associated with a served view being connected
* to an input method
@@ -1172,15 +1177,12 @@
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res != null) {
if (res.id != null) {
+ setInputChannelLocked(res.channel);
mBindSequence = res.sequence;
mCurMethod = res.method;
- if (mCurChannel != null) {
- mCurChannel.dispose();
- }
- mCurChannel = res.channel;
mCurId = res.id;
} else {
- if (res.channel != null) {
+ if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
if (mCurMethod == null) {
@@ -1653,8 +1655,13 @@
private void enqueuePendingEventLocked(
long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
PendingEvent p = obtainPendingEventLocked(startTime, seq, inputMethodId, callback);
- p.mNext = mFirstPendingEvent;
- mFirstPendingEvent = p;
+ if (mPendingEventTail != null) {
+ mPendingEventTail.mNext = p;
+ mPendingEventTail = p;
+ } else {
+ mPendingEventHead = p;
+ mPendingEventTail = p;
+ }
Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, seq, 0, p);
msg.setAsynchronous(true);
@@ -1662,12 +1669,15 @@
}
private PendingEvent dequeuePendingEventLocked(int seq) {
- PendingEvent p = mFirstPendingEvent;
+ PendingEvent p = mPendingEventHead;
if (p == null) {
return null;
}
if (p.mSeq == seq) {
- mFirstPendingEvent = p.mNext;
+ mPendingEventHead = p.mNext;
+ if (mPendingEventHead == null) {
+ mPendingEventTail = null;
+ }
} else {
PendingEvent prev;
do {
@@ -1678,6 +1688,9 @@
}
} while (p.mSeq != seq);
prev.mNext = p.mNext;
+ if (mPendingEventTail == p) {
+ mPendingEventTail = prev;
+ }
}
p.mNext = null;
return p;
@@ -1712,6 +1725,18 @@
}
}
+ private void flushPendingEventsLocked() {
+ mH.removeMessages(MSG_EVENT_TIMEOUT);
+
+ PendingEvent p = mPendingEventHead;
+ while (p != null) {
+ Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, p.mSeq, 0, p);
+ msg.setAsynchronous(true);
+ mH.sendMessage(msg);
+ p = p.mNext;
+ }
+ }
+
public void showInputMethodPicker() {
synchronized (mH) {
showInputMethodPickerLocked();
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
index 684ec07..17eb2df 100644
--- a/core/java/android/webkit/HTML5Audio.java
+++ b/core/java/android/webkit/HTML5Audio.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -84,6 +85,7 @@
// See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
private Timer mTimer;
private final class TimeupdateTask extends TimerTask {
+ @Override
public void run() {
HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget();
}
@@ -139,11 +141,13 @@
// (i.e. the webviewcore thread here)
// MediaPlayer.OnBufferingUpdateListener
+ @Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
nativeOnBuffering(percent, mNativePointer);
}
// MediaPlayer.OnCompletionListener;
+ @Override
public void onCompletion(MediaPlayer mp) {
mState = COMPLETE;
mProcessingOnEnd = true;
@@ -156,6 +160,7 @@
}
// MediaPlayer.OnErrorListener
+ @Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mState = ERROR;
resetMediaPlayer();
@@ -164,6 +169,7 @@
}
// MediaPlayer.OnPreparedListener
+ @Override
public void onPrepared(MediaPlayer mp) {
mState = PREPARED;
if (mTimer != null) {
@@ -178,6 +184,7 @@
}
// MediaPlayer.OnSeekCompleteListener
+ @Override
public void onSeekComplete(MediaPlayer mp) {
nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer);
}
@@ -231,7 +238,7 @@
headers.put(HIDE_URL_LOGS, "true");
}
- mMediaPlayer.setDataSource(url, headers);
+ mMediaPlayer.setDataSource(mContext, Uri.parse(url), headers);
mState = INITIALIZED;
mMediaPlayer.prepareAsync();
} catch (IOException e) {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 18df0b1..00d87bd 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -25,8 +25,13 @@
/**
* Top level factory, used creating all the main WebView implementation classes.
+ *
+ * @hide
*/
-class WebViewFactory {
+public final class WebViewFactory {
+ public static final String WEBVIEW_EXPERIMENTAL_PROPERTY = "persist.sys.webview.exp";
+ private static final String DEPRECATED_CHROMIUM_PROPERTY = "webview.use_chromium";
+
// Default Provider factory class name.
// TODO: When the Chromium powered WebView is ready, it should be the default factory class.
private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory";
@@ -43,16 +48,17 @@
private static WebViewFactoryProvider sProviderInstance;
private static final Object sProviderLock = new Object();
+ public static boolean isExperimentalWebViewAvailable() {
+ return Build.IS_DEBUGGABLE && (new java.io.File(CHROMIUM_WEBVIEW_JAR).exists());
+ }
+
static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
// For now the main purpose of this function (and the factory abstraction) is to keep
// us honest and minimize usage of WebViewClassic internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
- // For debug builds, we allow a system property to specify that we should use the
- // Chromium powered WebView. This enables us to switch between implementations
- // at runtime. For user (release) builds, don't allow this.
- if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("webview.use_chromium", false)) {
+ if (isExperimentalWebViewEnabled()) {
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try {
sProviderInstance = loadChromiumProvider();
@@ -76,6 +82,20 @@
}
}
+ // For debug builds, we allow a system property to specify that we should use the
+ // experimtanl Chromium powered WebView. This enables us to switch between
+ // implementations at runtime. For user (release) builds, don't allow this.
+ private static boolean isExperimentalWebViewEnabled() {
+ if (!isExperimentalWebViewAvailable())
+ return false;
+ if (SystemProperties.getBoolean(DEPRECATED_CHROMIUM_PROPERTY, false)) {
+ Log.w(LOGTAG, String.format("The property %s has been deprecated. Please use %s.",
+ DEPRECATED_CHROMIUM_PROPERTY, WEBVIEW_EXPERIMENTAL_PROPERTY));
+ return true;
+ }
+ return SystemProperties.getBoolean(WEBVIEW_EXPERIMENTAL_PROPERTY, false);
+ }
+
// TODO: This allows us to have the legacy and Chromium WebView coexist for development
// and side-by-side testing. After transition, remove this when no longer required.
private static WebViewFactoryProvider loadChromiumProvider() {
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index b2073b1..34cfea5 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -20,6 +20,7 @@
import android.app.AlertDialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -77,6 +78,7 @@
private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();
private final CharSequence mNewPermPrefix;
+ private String mPackageName;
static class MyPermissionGroupInfo extends PermissionGroupInfo {
CharSequence mLabel;
@@ -138,6 +140,8 @@
MyPermissionGroupInfo mGroup;
MyPermissionInfo mPerm;
AlertDialog mDialog;
+ private boolean mShowRevokeUI = false;
+ private String mPackageName;
public PermissionItemView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -145,9 +149,12 @@
}
public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm,
- boolean first, CharSequence newPermPrefix) {
+ boolean first, CharSequence newPermPrefix, String packageName,
+ boolean showRevokeUI) {
mGroup = grp;
mPerm = perm;
+ mShowRevokeUI = showRevokeUI;
+ mPackageName = packageName;
ImageView permGrpIcon = (ImageView) findViewById(R.id.perm_icon);
TextView permNameView = (TextView) findViewById(R.id.perm_name);
@@ -206,6 +213,7 @@
}
builder.setCancelable(true);
builder.setIcon(mGroup.loadGroupIcon(pm));
+ addRevokeUIIfNecessary(builder);
mDialog = builder.show();
mDialog.setCanceledOnTouchOutside(true);
}
@@ -218,6 +226,30 @@
mDialog.dismiss();
}
}
+
+ private void addRevokeUIIfNecessary(AlertDialog.Builder builder) {
+ if (!mShowRevokeUI) {
+ return;
+ }
+
+ final boolean isRequired =
+ ((mPerm.mExistingReqFlags & PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
+
+ if (isRequired) {
+ return;
+ }
+
+ DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ PackageManager pm = getContext().getPackageManager();
+ pm.revokePermission(mPackageName, mPerm.name);
+ PermissionItemView.this.setVisibility(View.GONE);
+ }
+ };
+ builder.setNegativeButton(R.string.revoke, ocl);
+ builder.setPositiveButton(R.string.ok, null);
+ }
}
private AppSecurityPermissions(Context context) {
@@ -230,6 +262,7 @@
public AppSecurityPermissions(Context context, String packageName) {
this(context);
+ mPackageName = packageName;
Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
PackageInfo pkgInfo;
try {
@@ -252,6 +285,7 @@
if(info == null) {
return;
}
+ mPackageName = info.packageName;
// Convert to a PackageInfo
PackageInfo installedPkgInfo = null;
@@ -419,15 +453,23 @@
}
public View getPermissionsView() {
- return getPermissionsView(WHICH_ALL);
+ return getPermissionsView(WHICH_ALL, false);
+ }
+
+ public View getPermissionsViewWithRevokeButtons() {
+ return getPermissionsView(WHICH_ALL, true);
}
public View getPermissionsView(int which) {
+ return getPermissionsView(which, false);
+ }
+
+ private View getPermissionsView(int which, boolean showRevokeUI) {
LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list);
View noPermsView = permsView.findViewById(R.id.no_permissions);
- displayPermissions(mPermGroupsList, displayList, which);
+ displayPermissions(mPermGroupsList, displayList, which, showRevokeUI);
if (displayList.getChildCount() <= 0) {
noPermsView.setVisibility(View.VISIBLE);
}
@@ -440,7 +482,7 @@
* list of permission descriptions.
*/
private void displayPermissions(List<MyPermissionGroupInfo> groups,
- LinearLayout permListView, int which) {
+ LinearLayout permListView, int which, boolean showRevokeUI) {
permListView.removeAllViews();
int spacing = (int)(8*mContext.getResources().getDisplayMetrics().density);
@@ -451,7 +493,7 @@
for (int j=0; j<perms.size(); j++) {
MyPermissionInfo perm = perms.get(j);
View view = getPermissionItemView(grp, perm, j == 0,
- which != WHICH_NEW ? mNewPermPrefix : null);
+ which != WHICH_NEW ? mNewPermPrefix : null, showRevokeUI);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -470,18 +512,19 @@
}
private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp,
- MyPermissionInfo perm, boolean first, CharSequence newPermPrefix) {
- return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix);
+ MyPermissionInfo perm, boolean first, CharSequence newPermPrefix, boolean showRevokeUI) {
+ return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix,
+ mPackageName, showRevokeUI);
}
private static PermissionItemView getPermissionItemView(Context context, LayoutInflater inflater,
MyPermissionGroupInfo grp, MyPermissionInfo perm, boolean first,
- CharSequence newPermPrefix) {
- PermissionItemView permView = (PermissionItemView)inflater.inflate(
+ CharSequence newPermPrefix, String packageName, boolean showRevokeUI) {
+ PermissionItemView permView = (PermissionItemView)inflater.inflate(
(perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0
? R.layout.app_permission_item_money : R.layout.app_permission_item,
null);
- permView.setPermission(grp, perm, first, newPermPrefix);
+ permView.setPermission(grp, perm, first, newPermPrefix, packageName, showRevokeUI);
return permView;
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 4b62c2d..c7914f3 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2433,7 +2433,7 @@
mFirstPosition;
} else {
final int lastPos = mFirstPosition + getChildCount() - 1;
- nextSelected = selectedPos != INVALID_POSITION && selectedPos < lastPos?
+ nextSelected = selectedPos != INVALID_POSITION && selectedPos <= lastPos ?
selectedPos - 1 :
lastPos;
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 529de2e..3df7258 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -220,28 +220,29 @@
// with MeasureSpec value overflow and RelativeLayout was one source of them.
// Some apps came to rely on them. :(
private boolean mAllowBrokenMeasureSpecs = false;
+ // Compatibility hack. Old versions of the platform would not take
+ // margins and padding into account when generating the height measure spec
+ // for children during the horizontal measure pass.
+ private boolean mMeasureVerticalWithPaddingMargin = false;
// A default width used for RTL measure pass
- private static int DEFAULT_WIDTH = Integer.MAX_VALUE / 2;
+ private static final int DEFAULT_WIDTH = Integer.MAX_VALUE / 2;
public RelativeLayout(Context context) {
super(context);
- mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
- Build.VERSION_CODES.JELLY_BEAN_MR1;
+ queryCompatibilityModes(context);
}
public RelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initFromAttributes(context, attrs);
- mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
- Build.VERSION_CODES.JELLY_BEAN_MR1;
+ queryCompatibilityModes(context);
}
public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initFromAttributes(context, attrs);
- mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
- Build.VERSION_CODES.JELLY_BEAN_MR1;
+ queryCompatibilityModes(context);
}
private void initFromAttributes(Context context, AttributeSet attrs) {
@@ -251,6 +252,12 @@
a.recycle();
}
+ private void queryCompatibilityModes(Context context) {
+ int version = context.getApplicationInfo().targetSdkVersion;
+ mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
+ mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
+ }
+
@Override
public boolean shouldDelayChildPressedState() {
return false;
@@ -692,6 +699,11 @@
params.leftMargin, params.rightMargin,
mPaddingLeft, mPaddingRight,
myWidth);
+ int maxHeight = myHeight;
+ if (mMeasureVerticalWithPaddingMargin) {
+ maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom -
+ params.topMargin - params.bottomMargin);
+ }
int childHeightMeasureSpec;
if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
if (params.height >= 0) {
@@ -704,9 +716,9 @@
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
} else if (params.width == LayoutParams.MATCH_PARENT) {
- childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
} else {
- childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
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.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 @@
* @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/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index 4045497..62afd2e 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -39,31 +39,26 @@
* <p>
* Here is how to use the action provider with custom backing file in a {@link MenuItem}:
* </p>
- * <p>
* <pre>
- * <code>
- * // In Activity#onCreateOptionsMenu
- * public boolean onCreateOptionsMenu(Menu menu) {
- * // Get the menu item.
- * MenuItem menuItem = menu.findItem(R.id.my_menu_item);
- * // Get the provider and hold onto it to set/change the share intent.
- * mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider();
- * // Set history different from the default before getting the action
- * // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls
- * // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
- * // line if using the default share history file is desired.
- * mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
- * . . .
- * }
+ * // In Activity#onCreateOptionsMenu
+ * public boolean onCreateOptionsMenu(Menu menu) {
+ * // Get the menu item.
+ * MenuItem menuItem = menu.findItem(R.id.my_menu_item);
+ * // Get the provider and hold onto it to set/change the share intent.
+ * mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider();
+ * // Set history different from the default before getting the action
+ * // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls
+ * // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
+ * // line if using the default share history file is desired.
+ * mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
+ * . . .
+ * }
*
- * // Somewhere in the application.
- * public void doShare(Intent shareIntent) {
- * // When you want to share set the share intent.
- * mShareActionProvider.setShareIntent(shareIntent);
- * }
- * </pre>
- * </code>
- * </p>
+ * // Somewhere in the application.
+ * public void doShare(Intent shareIntent) {
+ * // When you want to share set the share intent.
+ * mShareActionProvider.setShareIntent(shareIntent);
+ * }</pre>
* <p>
* <strong>Note:</strong> While the sample snippet demonstrates how to use this provider
* in the context of a menu item, the use of the provider is not limited to menu items.
@@ -245,9 +240,9 @@
* call {@link android.app.Activity#invalidateOptionsMenu()} to recreate the
* action view. You should <strong>not</strong> call
* {@link android.app.Activity#invalidateOptionsMenu()} from
- * {@link android.app.Activity#onCreateOptionsMenu(Menu)}."
- * <p>
- * <code>
+ * {@link android.app.Activity#onCreateOptionsMenu(Menu)}.
+ * </p>
+ * <pre>
* private void doShare(Intent intent) {
* if (IMAGE.equals(intent.getMimeType())) {
* mShareActionProvider.setHistoryFileName(SHARE_IMAGE_HISTORY_FILE_NAME);
@@ -256,9 +251,7 @@
* }
* mShareActionProvider.setIntent(intent);
* invalidateOptionsMenu();
- * }
- * <code>
- *
+ * }</pre>
* @param shareHistoryFile The share history file name.
*/
public void setShareHistoryFileName(String shareHistoryFile) {
@@ -269,16 +262,11 @@
/**
* Sets an intent with information about the share action. Here is a
* sample for constructing a share intent:
- * <p>
* <pre>
- * <code>
- * Intent shareIntent = new Intent(Intent.ACTION_SEND);
- * shareIntent.setType("image/*");
- * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
- * shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());
- * </pre>
- * </code>
- * </p>
+ * Intent shareIntent = new Intent(Intent.ACTION_SEND);
+ * shareIntent.setType("image/*");
+ * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
+ * shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());</pre>
*
* @param shareIntent The share intent.
*
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index db20549..acbb2b1 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -812,6 +812,26 @@
return mActionView != null && mActionView.isTitleTruncated();
}
+ @Override
+ public void setHomeAsUpIndicator(Drawable indicator) {
+ mActionView.setHomeAsUpIndicator(indicator);
+ }
+
+ @Override
+ public void setHomeAsUpIndicator(int resId) {
+ mActionView.setHomeAsUpIndicator(resId);
+ }
+
+ @Override
+ public void setHomeActionContentDescription(CharSequence description) {
+ mActionView.setHomeActionContentDescription(description);
+ }
+
+ @Override
+ public void setHomeActionContentDescription(int resId) {
+ mActionView.setHomeActionContentDescription(resId);
+ }
+
/**
* @hide
*/
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
new file mode 100644
index 0000000..e26b27d
--- /dev/null
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -0,0 +1,146 @@
+/*
+**
+** 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.
+*/
+
+package com.android.internal.os;
+
+import java.io.PrintStream;
+
+public abstract class BaseCommand {
+
+ protected String[] mArgs;
+ private int mNextArg;
+ private String mCurArgData;
+
+ // These are magic strings understood by the Eclipse plugin.
+ public static final String FATAL_ERROR_CODE = "Error type 1";
+ public static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
+ public static final String NO_CLASS_ERROR_CODE = "Error type 3";
+
+ /**
+ * Call to run the command.
+ */
+ public void run(String[] args) {
+ if (args.length < 1) {
+ onShowUsage(System.out);
+ return;
+ }
+
+ mArgs = args;
+ mNextArg = 0;
+ mCurArgData = null;
+
+ try {
+ onRun();
+ } catch (IllegalArgumentException e) {
+ onShowUsage(System.err);
+ System.err.println();
+ System.err.println("Error: " + e.getMessage());
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Convenience to show usage information to error output.
+ */
+ public void showUsage() {
+ onShowUsage(System.err);
+ }
+
+ /**
+ * Convenience to show usage information to error output along
+ * with an error message.
+ */
+ public void showError(String message) {
+ onShowUsage(System.err);
+ System.err.println();
+ System.err.println(message);
+ }
+
+ /**
+ * Implement the command.
+ */
+ public abstract void onRun() throws Exception;
+
+ /**
+ * Print help text for the command.
+ */
+ public abstract void onShowUsage(PrintStream out);
+
+ /**
+ * Return the next option on the command line -- that is an argument that
+ * starts with '-'. If the next argument is not an option, null is returned.
+ */
+ public String nextOption() {
+ if (mCurArgData != null) {
+ String prev = mArgs[mNextArg - 1];
+ throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
+ }
+ if (mNextArg >= mArgs.length) {
+ return null;
+ }
+ String arg = mArgs[mNextArg];
+ if (!arg.startsWith("-")) {
+ return null;
+ }
+ mNextArg++;
+ if (arg.equals("--")) {
+ return null;
+ }
+ if (arg.length() > 1 && arg.charAt(1) != '-') {
+ if (arg.length() > 2) {
+ mCurArgData = arg.substring(2);
+ return arg.substring(0, 2);
+ } else {
+ mCurArgData = null;
+ return arg;
+ }
+ }
+ mCurArgData = null;
+ return arg;
+ }
+
+ /**
+ * Return the next argument on the command line, whatever it is; if there are
+ * no arguments left, return null.
+ */
+ public String nextArg() {
+ if (mCurArgData != null) {
+ String arg = mCurArgData;
+ mCurArgData = null;
+ return arg;
+ } else if (mNextArg < mArgs.length) {
+ return mArgs[mNextArg++];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return the next argument on the command line, whatever it is; if there are
+ * no arguments left, throws an IllegalArgumentException to report this to the user.
+ */
+ public String nextArgRequired() {
+ String arg = nextArg();
+ if (arg == null) {
+ String prev = mArgs[mNextArg - 1];
+ throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
+ }
+ return arg;
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 780f5b3..58b15e2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -17,7 +17,7 @@
package com.android.internal.statusbar;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
+import android.service.notification.StatusBarNotification;
/** @hide */
oneway interface IStatusBar
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 04e5bc9..c98ba8d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -19,7 +19,7 @@
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.statusbar.StatusBarNotification;
+import android.service.notification.StatusBarNotification;
/** @hide */
interface IStatusBarService
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 9143c61..14afe21 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -84,7 +84,7 @@
dest.writeStrongInterface(method);
if (channel != null) {
dest.writeInt(1);
- channel.writeToParcel(dest, 0);
+ channel.writeToParcel(dest, flags);
} else {
dest.writeInt(0);
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
index 2685046..7ca6c1b 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItem.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -107,7 +107,7 @@
}
public CharSequence getTitleCondensed() {
- return mTitleCondensed;
+ return mTitleCondensed != null ? mTitleCondensed : mTitle;
}
public boolean hasSubMenu() {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index d1db230..59ff597 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -36,7 +36,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.TypedArray;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -94,6 +93,8 @@
private CharSequence mSubtitle;
private Drawable mIcon;
private Drawable mLogo;
+ private CharSequence mHomeDescription;
+ private int mHomeDescriptionRes;
private HomeView mHomeLayout;
private HomeView mExpandedHomeLayout;
@@ -125,6 +126,7 @@
private boolean mWasHomeEnabled; // Was it enabled before action view expansion?
private MenuBuilder mOptionsMenu;
+ private boolean mMenuPrepared;
private ActionBarContextView mContextView;
@@ -164,7 +166,10 @@
private final OnClickListener mUpClickListener = new OnClickListener() {
public void onClick(View v) {
- mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
+ if (mMenuPrepared) {
+ // Only invoke the window callback if the options menu has been initialized.
+ mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
+ }
}
};
@@ -285,6 +290,10 @@
initTitle();
}
+ if (mHomeDescriptionRes != 0) {
+ setHomeActionContentDescription(mHomeDescriptionRes);
+ }
+
if (mTabScrollView != null && mIncludeTabs) {
ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams();
if (lp != null) {
@@ -402,6 +411,10 @@
mCallback = callback;
}
+ public void setMenuPrepared() {
+ mMenuPrepared = true;
+ }
+
public void setMenu(Menu menu, MenuPresenter.Callback cb) {
if (menu == mOptionsMenu) return;
@@ -582,14 +595,43 @@
mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
} else {
mUpGoerFive.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ mUpGoerFive.setContentDescription(buildHomeContentDescription());
+ }
+ }
+
+ /**
+ * Compose a content description for the Home/Up affordance.
+ *
+ * <p>As this encompasses the icon/logo, title and subtitle all in one, we need
+ * a description for the whole wad of stuff that can be localized properly.</p>
+ */
+ private CharSequence buildHomeContentDescription() {
+ final CharSequence homeDesc;
+ if (mHomeDescription != null) {
+ homeDesc = mHomeDescription;
+ } else {
if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
- mUpGoerFive.setContentDescription(mContext.getResources().getText(
- R.string.action_bar_up_description));
+ homeDesc = mContext.getResources().getText(R.string.action_bar_up_description);
} else {
- mUpGoerFive.setContentDescription(mContext.getResources().getText(
- R.string.action_bar_home_description));
+ homeDesc = mContext.getResources().getText(R.string.action_bar_home_description);
}
}
+
+ final CharSequence title = getTitle();
+ final CharSequence subtitle = getSubtitle();
+ if (!TextUtils.isEmpty(title)) {
+ final String result;
+ if (!TextUtils.isEmpty(subtitle)) {
+ result = getResources().getString(
+ R.string.action_bar_home_subtitle_description_format,
+ title, subtitle, homeDesc);
+ } else {
+ result = getResources().getString(R.string.action_bar_home_description_format,
+ title, homeDesc);
+ }
+ return result;
+ }
+ return homeDesc;
}
public void setDisplayOptions(int options) {
@@ -1298,6 +1340,23 @@
}
}
+ public void setHomeAsUpIndicator(Drawable indicator) {
+ mHomeLayout.setUpIndicator(indicator);
+ }
+
+ public void setHomeAsUpIndicator(int resId) {
+ mHomeLayout.setUpIndicator(resId);
+ }
+
+ public void setHomeActionContentDescription(CharSequence description) {
+ mHomeDescription = description;
+ }
+
+ public void setHomeActionContentDescription(int resId) {
+ mHomeDescriptionRes = resId;
+ mHomeDescription = getResources().getText(resId);
+ }
+
static class SavedState extends BaseSavedState {
int expandedMenuItemId;
boolean isOverflowOpen;
@@ -1332,9 +1391,11 @@
}
private static class HomeView extends FrameLayout {
- private View mUpView;
+ private ImageView mUpView;
private ImageView mIconView;
private int mUpWidth;
+ private int mUpIndicatorRes;
+ private Drawable mDefaultUpIndicator;
private static final long DEFAULT_TRANSITION_DURATION = 150;
@@ -1359,6 +1420,25 @@
mIconView.setImageDrawable(icon);
}
+ public void setUpIndicator(Drawable d) {
+ mUpView.setImageDrawable(d != null ? d : mDefaultUpIndicator);
+ mUpIndicatorRes = 0;
+ }
+
+ public void setUpIndicator(int resId) {
+ mUpIndicatorRes = resId;
+ mUpView.setImageDrawable(resId != 0 ? getResources().getDrawable(resId) : null);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mUpIndicatorRes != 0) {
+ // Reload for config change
+ setUpIndicator(mUpIndicatorRes);
+ }
+ }
+
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
onPopulateAccessibilityEvent(event);
@@ -1382,8 +1462,9 @@
@Override
protected void onFinishInflate() {
- mUpView = findViewById(com.android.internal.R.id.up);
+ mUpView = (ImageView) findViewById(com.android.internal.R.id.up);
mIconView = (ImageView) findViewById(com.android.internal.R.id.home);
+ mDefaultUpIndicator = mUpView.getDrawable();
}
public int getStartOffset() {
diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java
index 8ebe94c..ca797eb 100644
--- a/core/java/com/android/internal/widget/TransportControlView.java
+++ b/core/java/com/android/internal/widget/TransportControlView.java
@@ -158,7 +158,7 @@
}
}
- 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_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 weakreference_offsets_t
-{
- // Class state.
- jclass mClass;
- jmethodID mGet;
-
-} gWeakReferenceOffsets;
-
static struct error_offsets_t
{
jclass mClass;
@@ -570,7 +562,7 @@
// 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 @@
{
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_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 9c44a59..9fa9fe4 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -246,6 +246,15 @@
return name;
}
+static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ if (nativeInputChannel) {
+ android_view_InputChannel_setNativeInputChannel(env, otherObj,
+ new NativeInputChannel(nativeInputChannel->getInputChannel()->dup()));
+ }
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gInputChannelMethods[] = {
@@ -262,6 +271,8 @@
(void*)android_view_InputChannel_nativeWriteToParcel },
{ "nativeGetName", "()Ljava/lang/String;",
(void*)android_view_InputChannel_nativeGetName },
+ { "nativeDup", "(Landroid/view/InputChannel;)V",
+ (void*)android_view_InputChannel_nativeDup },
};
#define FIND_CLASS(var, className) \
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 576f831..e3a54a8 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -62,8 +62,8 @@
const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
for (size_t i = 0; i < ranges.size(); i++) {
const InputDeviceInfo::MotionRange& range = ranges.itemAt(i);
- env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.addMotionRange,
- range.axis, range.source, range.min, range.max, range.flat, range.fuzz);
+ env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.addMotionRange, range.axis,
+ range.source, range.min, range.max, range.flat, range.fuzz, range.resolution);
if (env->ExceptionCheck()) {
return NULL;
}
@@ -90,7 +90,7 @@
"<init>", "(IILjava/lang/String;Ljava/lang/String;ZIILandroid/view/KeyCharacterMap;Z)V");
GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
- "addMotionRange", "(IIFFFF)V");
+ "addMotionRange", "(IIFFFFF)V");
return 0;
}
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 @@
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 @@
virtual ~NativeInputEventReceiver();
private:
- jobject mReceiverObjGlobal;
+ jobject mReceiverWeakGlobal;
InputConsumer mInputConsumer;
sp<MessageQueue> mMessageQueue;
PreallocatedInputEventFactory mInputEventFactory;
@@ -74,9 +76,9 @@
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 = AndroidRuntime::getJNIEnv();
- env->DeleteGlobalRef(mReceiverObjGlobal);
+ env->DeleteGlobalRef(mReceiverWeakGlobal);
}
status_t NativeInputEventReceiver::initialize() {
@@ -151,6 +153,7 @@
mBatchedInputEventPending = false;
}
+ ScopedLocalRef<jobject> receiverObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
uint32_t seq;
@@ -162,12 +165,21 @@
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 @@
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 @@
#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 @@
}
-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 @@
}
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 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 @@
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 @@
virtual ~NativeInputEventSender();
private:
- jobject mSenderObjGlobal;
+ jobject mSenderWeakGlobal;
InputPublisher mInputPublisher;
sp<MessageQueue> mMessageQueue;
KeyedVector<uint32_t, uint32_t> mPublishedSeqMap;
@@ -75,11 +77,11 @@
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 = AndroidRuntime::getJNIEnv();
- env->DeleteGlobalRef(mSenderObjGlobal);
+ env->DeleteGlobalRef(mSenderWeakGlobal);
}
status_t NativeInputEventSender::initialize() {
@@ -178,6 +180,7 @@
ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName());
#endif
+ ScopedLocalRef<jobject> senderObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
uint32_t publishedSeq;
@@ -205,7 +208,16 @@
#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 @@
}
-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 @@
}
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 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 666d1c6..90e3b8d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2193,6 +2193,14 @@
android:description="@string/permdesc_accessNotifications"
android:protectionLevel="signature|system" />
+ <!-- Must be required by an {@link
+ android.service.notification.NotificationListenerService},
+ to ensure that only the system can bind to it. -->
+ <permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+ android:label="@string/permlab_bindNotificationListenerService"
+ android:description="@string/permdesc_bindNotificationListenerService"
+ android:protectionLevel="signature" />
+
<!-- The system process is explicitly the only one allowed to launch the
confirmation UI for full backup/restore -->
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
@@ -2301,6 +2309,12 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.updates.IntentFirewallInstallReceiver" >
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_INTENT_FIREWALL" />
+ </intent-filter>
+ </receiver>
+
<receiver android:name="com.android.server.updates.SmsShortCodesInstallReceiver" >
<intent-filter>
<action android:name="android.intent.action.UPDATE_SMS_SHORT_CODES" />
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.png
new file mode 100644
index 0000000..7c5826f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.png
new file mode 100644
index 0000000..974a292
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.png
new file mode 100644
index 0000000..b3196c9
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.png
new file mode 100644
index 0000000..1f833d3
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.png
new file mode 100644
index 0000000..e969abc
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.png
new file mode 100644
index 0000000..3adbc84
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.png
new file mode 100644
index 0000000..a321836
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.png
new file mode 100644
index 0000000..4c5d692
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.png
new file mode 100644
index 0000000..6199dc5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.png
new file mode 100644
index 0000000..1b0905a
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.png
new file mode 100644
index 0000000..c6d7868
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.png
new file mode 100644
index 0000000..179644c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.png
new file mode 100644
index 0000000..039a056
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.png
new file mode 100644
index 0000000..c8d68c5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.png
new file mode 100644
index 0000000..1fef1ad
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.png
new file mode 100644
index 0000000..6b22d44
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.png
new file mode 100644
index 0000000..c219527
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.png
new file mode 100644
index 0000000..2a1d508
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/layout/select_dialog.xml b/core/res/res/layout/select_dialog.xml
index 80d22f6..eb4d8d9 100644
--- a/core/res/res/layout/select_dialog.xml
+++ b/core/res/res/layout/select_dialog.xml
@@ -32,4 +32,5 @@
android:cacheColorHint="@null"
android:divider="?android:attr/listDividerAlertDialog"
android:scrollbars="vertical"
- android:overScrollMode="ifContentScrolls" />
+ android:overScrollMode="ifContentScrolls"
+ android:textAlignment="viewStart" />
diff --git a/core/res/res/layout/select_dialog_holo.xml b/core/res/res/layout/select_dialog_holo.xml
index 06a5d96..8a92283 100644
--- a/core/res/res/layout/select_dialog_holo.xml
+++ b/core/res/res/layout/select_dialog_holo.xml
@@ -30,4 +30,5 @@
android:cacheColorHint="@null"
android:divider="?android:attr/listDividerAlertDialog"
android:scrollbars="vertical"
- android:overScrollMode="ifContentScrolls" />
+ android:overScrollMode="ifContentScrolls"
+ android:textAlignment="viewStart" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 8a956d0..1e266ed 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1470,10 +1470,13 @@
<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>
- <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Verhoog volume bo aanbevole vlak?"\n"Deur vir lang tydperke na hoë volume te luister, kan jou gehoor beskadig word."</string>
+ <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>
+ <string name="error_message_title" msgid="4510373083082500195">"Fout"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Hierdie program werk nie met rekeninge vir beperkte gebruikers nie"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Geen program gevind om hierdie handeling te hanteer nie"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 103f0b7..54dfd50e 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,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-ar/strings.xml b/core/res/res/values-ar/strings.xml
index b8fc281..0c6cd78 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,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-be/strings.xml b/core/res/res/values-be/strings.xml
index eb7c6cd..e6c9ee7 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1476,4 +1476,7 @@
<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 365186c..975dc40 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1476,4 +1476,7 @@
<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 621067f..0adebe9 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1476,4 +1476,7 @@
<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 3dcd8f3..d2676c9 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -201,7 +201,7 @@
<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>
<string name="permgrouplab_screenlock" msgid="8275500173330718168">"Lås skærm"</string>
- <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Evne til at påvirke låseskærmens adfærd på enheden."</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>
@@ -1143,7 +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>
- <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"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="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>
@@ -1476,4 +1476,7 @@
<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>
+ <string name="error_message_title" msgid="4510373083082500195">"Fejl"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Denne applikation understøtter ikke konti for brugere med begrænsede rettigheder"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Der blev ikke fundet nogen applikation, der kan håndtere denne handling"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index e15a39f..6a488c7 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1476,4 +1476,7 @@
<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 2dbb82b..21b014c 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1476,4 +1476,7 @@
<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 9fab86e..ae698e1 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1476,4 +1476,7 @@
<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 ee18e9e..f3f0afc 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1476,4 +1476,7 @@
<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>
+ <string name="error_message_title" msgid="4510373083082500195">"Error"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Esta aplicación no admite cuentas para usuarios restringidos."</string>
+ <string name="app_not_found" msgid="3429141853498927379">"No se encontró una aplicación para manejar esta acción."</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 50fb83c3..d3e63af 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1476,4 +1476,7 @@
<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>
+ <string name="error_message_title" msgid="4510373083082500195">"Error"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Esta aplicación no admite cuentas de usuarios limitados."</string>
+ <string name="app_not_found" msgid="3429141853498927379">"No se ha encontrado ninguna aplicación que pueda realizar esta acción."</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 3abaf62e..bb00bfc 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 õigustega kasutajate kontosid"</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..322cbee 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,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-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 1bc0fe4..2026f56 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1476,4 +1476,7 @@
<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..1b3e6d7 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,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-hr/strings.xml b/core/res/res/values-hr/strings.xml
index c196b59..7b1eee3 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1143,7 +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>
- <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tabletno računalo privremeno će se isključiti s Wi-Fija dok je povezano s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <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>
@@ -1476,4 +1476,7 @@
<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 ff978a0..fc1d663 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1476,4 +1476,7 @@
<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 fee8fc4..2a9f2fc 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -200,8 +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>
- <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Layar pengunci"</string>
- <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Kemampuan untuk memengaruhi perilaku layar pengunci di perangkat Anda."</string>
+ <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>
@@ -1476,4 +1476,7 @@
<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 0cd7322..2273459 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1476,4 +1476,7 @@
<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 0139b29..8c7c74e 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1476,4 +1476,7 @@
<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 af64842..4ec756e 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1476,4 +1476,7 @@
<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 5d663d5..e6010ac 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1476,4 +1476,7 @@
<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 5890f13..f134005 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1476,4 +1476,7 @@
<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-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 5fd9a42..9d39ac8 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1476,4 +1476,7 @@
<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 f5dd3be..b0a6d30 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1476,4 +1476,7 @@
<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 f6ffb20..0414be8 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -744,7 +744,7 @@
<string name="relationTypeDomesticPartner" msgid="6904807112121122133">"Partner życiowy"</string>
<string name="relationTypeFather" msgid="5228034687082050725">"Ojciec"</string>
<string name="relationTypeFriend" msgid="7313106762483391262">"Znajomy"</string>
- <string name="relationTypeManager" msgid="6365677861610137895">"Kierownik"</string>
+ <string name="relationTypeManager" msgid="6365677861610137895">"Menedżer"</string>
<string name="relationTypeMother" msgid="4578571352962758304">"Matka"</string>
<string name="relationTypeParent" msgid="4755635567562925226">"Rodzic"</string>
<string name="relationTypePartner" msgid="7266490285120262781">"Partner"</string>
@@ -1476,4 +1476,7 @@
<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 82a0893..5eba00c 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1476,4 +1476,7 @@
<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 fdeeb93..b11cc2f 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1476,4 +1476,7 @@
<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 da1f312..3da7211 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1056,8 +1056,8 @@
<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>
@@ -1476,4 +1476,7 @@
<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 94fdd7b..9ffe30d 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1476,4 +1476,7 @@
<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 1ec58d4..7e8cf01 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1476,4 +1476,7 @@
<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 6a51baf..5a54f71 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1476,4 +1476,7 @@
<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..67b4608 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>
@@ -1355,7 +1350,7 @@
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"Imefanyika"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Modi ya mabadiliko"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Songa"</string>
- <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Ingiza"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
<string name="activitychooserview_choose_application" msgid="2125168057199941199">"Chagua programu"</string>
<string name="shareactionprovider_share_with" msgid="806688056141131819">"Shiriki na"</string>
<string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Shiriki na <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
@@ -1475,11 +1470,13 @@
<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>
+ <string name="error_message_title" msgid="4510373083082500195">"Hitilafu"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Programu hii haiwezi kutumiwa na akaunti za watumiaji waliowekewa vizuizi"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Hakuna programu iliyopatikana ili kushughulikia kitendo hiki"</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4bca1b1..2d56e38 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -154,9 +154,9 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"ตัวเลือกโทรศัพท์"</string>
<string name="global_action_lock" msgid="2844945191792119712">"ล็อกหน้าจอ"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"ปิดเครื่อง"</string>
- <string name="global_action_bug_report" msgid="7934010578922304799">"รายงานข้อบกพร่อง"</string>
- <string name="bugreport_title" msgid="2667494803742548533">"ใช้รายงานข้อบกพร่อง"</string>
- <string name="bugreport_message" msgid="398447048750350456">"การดำเนินการนี้จะรวบรวมข้อมูลเกี่ยวกับสถานะปัจจุบันของอุปกรณ์ของคุณ โดยจะส่งไปในรูปแบบข้อความอีเมล อาจใช้เวลาสักครู่ตั้งแต่เริ่มการสร้างรายงานข้อบกพร่องจนกระทั่งเสร็จสมบูรณ์ โปรดอดทนรอ"</string>
+ <string name="global_action_bug_report" msgid="7934010578922304799">"รายงานบั๊ก"</string>
+ <string name="bugreport_title" msgid="2667494803742548533">"ใช้รายงานบั๊ก"</string>
+ <string name="bugreport_message" msgid="398447048750350456">"การดำเนินการนี้จะรวบรวมข้อมูลเกี่ยวกับสถานะปัจจุบันของอุปกรณ์ของคุณ โดยจะส่งไปในรูปแบบข้อความอีเมล อาจใช้เวลาสักครู่ตั้งแต่เริ่มการสร้างรายงานบั๊กจนกระทั่งเสร็จสมบูรณ์ โปรดอดทนรอ"</string>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"โหมดปิดเสียง"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"ปิดเสียงไว้"</string>
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"เปิดเสียงแล้ว"</string>
@@ -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-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 5354a54..beeb6c0 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1476,4 +1476,7 @@
<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 c5ce4cd..c8d3ae4 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1476,4 +1476,7 @@
<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>
+ <string name="error_message_title" msgid="4510373083082500195">"Hata"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Bu uygulama, kısıtlı kullanıcı hesaplarını desteklemiyor"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Bu eylemi gerçekleştirecek bir uygulama bulunamadı"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 7c8f192..6f505b1 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1476,4 +1476,7 @@
<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 e9ccd36..4fe8d65 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -201,7 +201,7 @@
<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>
<string name="permgrouplab_screenlock" msgid="8275500173330718168">"Khóa màn hình"</string>
- <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Khả năng ảnh hưởng tới trạng thái màn hình khóa trên thiết bị của bạn."</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>
@@ -1470,10 +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>
- <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 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="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 c768ec8..af1a2ea 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -201,7 +201,7 @@
<string name="permgrouplab_camera" msgid="4820372495894586615">"相机"</string>
<string name="permgroupdesc_camera" msgid="2933667372289567714">"直接使用相机以拍摄图片或视频。"</string>
<string name="permgrouplab_screenlock" msgid="8275500173330718168">"锁定屏幕"</string>
- <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"能够影响设备的锁定屏幕的行为。"</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>
@@ -1143,7 +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>
- <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="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>
@@ -1470,10 +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>
- <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"将音量调高到推荐级别以上?"\n"长时间使用高音量可能会损伤听力。"</string>
+ <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 5cb2f9d..cea9369 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>
@@ -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 d9d0913..8bf3aae 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1476,4 +1476,7 @@
<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>
+ <string name="error_message_title" msgid="4510373083082500195">"Iphutha"</string>
+ <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Lolu hlelo lokusebenza alusekeli ama-akhawunti wabasebenzisi abakhawulelwe"</string>
+ <string name="app_not_found" msgid="3429141853498927379">"Alukho uhlelo lokusebenza olutholakele lokuphatha lesi senzo"</string>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4a15967..a0e1603 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1816,6 +1816,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessNotifications">Allows the app to retrieve, examine, and clear notifications, including those posted by other apps.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_bindNotificationListenerService">bind to a notification listener service</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bindNotificationListenerService">Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps.</string>
+
<!-- Policy administration -->
<!-- Title of policy access to limiting the user's password choices -->
@@ -2992,11 +2997,11 @@
<!-- [CHAR LIMIT=NONE] Stub notification title for an app running a service that has provided
a bad bad notification for itself. -->
<string name="app_running_notification_title"><xliff:g id="app_name">%1$s</xliff:g>
- running</string>
+ is running</string>
<!-- [CHAR LIMIT=NONE] Stub notification text for an app running a service that has provided
a bad bad notification for itself. -->
- <string name="app_running_notification_text"><xliff:g id="app_name">%1$s</xliff:g>
- is currently running</string>
+ <string name="app_running_notification_text">Touch for more information
+ or to stop the app.</string>
<!-- Preference framework strings. -->
<string name="ok">OK</string>
@@ -3508,6 +3513,9 @@
<string name="wallpaper_binding_label">Wallpaper</string>
<!-- Dialog title for user to select a different wallpaper from service list -->
<string name="chooser_wallpaper">Change wallpaper</string>
+ <!-- Label to show for a service that is running because it is observing
+ the user's notifications. -->
+ <string name="notification_listener_binding_label">Notification listener</string>
<!-- Do Not Translate: Alternate eri.xml -->
<string name="alternate_eri_file">/data/eri.xml</string>
@@ -3763,6 +3771,17 @@
<string name="action_bar_up_description">Navigate up</string>
<!-- Content description for the action menu overflow button. [CHAR LIMIT=NONE] -->
<string name="action_menu_overflow_description">More options</string>
+ <!-- Formatting string for describing the action bar's title/home/up affordance.
+ This is a single tappable "button" that includes the app icon, the Up indicator
+ (usually a "<" chevron) and the window title text.
+ %1$s is the title. %2$s is the description of what tapping/clicking the whole
+ thing is going to do. -->
+ <string name="action_bar_home_description_format">%1$s, %2$s</string>
+ <!-- Just like action_bar_home_description_format, but this one will be used
+ if the window is also providing subtitle text.
+ %1$s is the title. %2$s is the subtitle. %3$s is the description of what
+ tapping/clicking the whole thing is going to do. -->
+ <string name="action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string>
<!-- Storage description for internal storage. [CHAR LIMIT=NONE] -->
<string name="storage_internal">Internal storage</string>
@@ -4069,4 +4088,5 @@
<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>
+ <string name="revoke">Revoke</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 757bbc8..e06bcd1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -710,6 +710,7 @@
<java-symbol type="string" name="relationTypeSpouse" />
<java-symbol type="string" name="relative_time" />
<java-symbol type="string" name="reset" />
+ <java-symbol type="string" name="revoke" />
<java-symbol type="string" name="ringtone_default" />
<java-symbol type="string" name="ringtone_default_with_actual" />
<java-symbol type="string" name="ringtone_picker_title" />
@@ -870,6 +871,8 @@
<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="string" name="action_bar_home_description_format" />
+ <java-symbol type="string" name="action_bar_home_subtitle_description_format" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
<java-symbol type="plurals" name="abbrev_in_num_hours" />
@@ -1142,6 +1145,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" />
@@ -1637,6 +1641,7 @@
<java-symbol type="string" name="launch_warning_title" />
<java-symbol type="string" name="low_internal_storage_view_text" />
<java-symbol type="string" name="low_internal_storage_view_title" />
+ <java-symbol type="string" name="notification_listener_binding_label" />
<java-symbol type="string" name="report" />
<java-symbol type="string" name="select_input_method" />
<java-symbol type="string" name="select_keyboard_layout_notification_title" />
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/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 @@
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);
+ public void testManifestDigest_Equals() throws Exception {
+ InputStream is = new ByteArrayInputStream(MESSAGE_1);
- a.put(MD5_DIGEST, DIGEST_2_STR);
+ ManifestDigest expected =
+ new ManifestDigest(MessageDigest.getInstance("SHA-256").digest(MESSAGE_1));
- ManifestDigest fromAttributes = ManifestDigest.fromAttributes(a);
+ ManifestDigest actual = ManifestDigest.fromInputStream(is);
+ assertEquals(expected, actual);
- assertNotNull("A valid ManifestDigest should be returned", fromAttributes);
-
- ManifestDigest created = new ManifestDigest(DIGEST_1);
-
- assertEquals("SHA-1 should be preferred over MD5: " + created.toString() + " vs. "
- + fromAttributes.toString(), created, fromAttributes);
-
- 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 @@
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 @@
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 @@
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 @@
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/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf
index 40ecd14..072b842 100644
--- a/data/fonts/Roboto-Bold.ttf
+++ b/data/fonts/Roboto-Bold.ttf
Binary files differ
diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf
index d9067c5..74919ff 100644
--- a/data/fonts/Roboto-BoldItalic.ttf
+++ b/data/fonts/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf
index 88e4a5b..bd57775 100644
--- a/data/fonts/Roboto-Italic.ttf
+++ b/data/fonts/Roboto-Italic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf
index 2ae4dec..13bf13a 100644
--- a/data/fonts/Roboto-Light.ttf
+++ b/data/fonts/Roboto-Light.ttf
Binary files differ
diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf
index 44177ef..130672a 100644
--- a/data/fonts/Roboto-LightItalic.ttf
+++ b/data/fonts/Roboto-LightItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf
index f592adf..0ba95c9 100644
--- a/data/fonts/Roboto-Regular.ttf
+++ b/data/fonts/Roboto-Regular.ttf
Binary files differ
diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf
index 5ae4d7f..309c22d 100644
--- a/data/fonts/Roboto-Thin.ttf
+++ b/data/fonts/Roboto-Thin.ttf
Binary files differ
diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf
index 9cd3927..0b53ba4 100644
--- a/data/fonts/Roboto-ThinItalic.ttf
+++ b/data/fonts/Roboto-ThinItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf
index 21c10f5..f0fd409 100644
--- a/data/fonts/RobotoCondensed-Bold.ttf
+++ b/data/fonts/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf
index d8edd2d..e67b02b 100644
--- a/data/fonts/RobotoCondensed-BoldItalic.ttf
+++ b/data/fonts/RobotoCondensed-BoldItalic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf
index 4dec2cf..a08414b 100644
--- a/data/fonts/RobotoCondensed-Italic.ttf
+++ b/data/fonts/RobotoCondensed-Italic.ttf
Binary files differ
diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf
index 875ea1a..713fd30 100644
--- a/data/fonts/RobotoCondensed-Regular.ttf
+++ b/data/fonts/RobotoCondensed-Regular.ttf
Binary files differ
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
+<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>
+
+<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 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>
+<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>
-<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>
-
+<div id="version-chart">
</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 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 @@
<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>
+
+<div id="screens-chart">
+</div>
-<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>
-
-
+<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 @@
<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 @@
<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 @@
</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/android-4.0.jd b/docs/html/about/versions/android-4.0.jd
index f2fd0c4..868227a 100644
--- a/docs/html/about/versions/android-4.0.jd
+++ b/docs/html/about/versions/android-4.0.jd
@@ -122,7 +122,7 @@
receiving the app uses it to invite the specified contact to that
social network. Most apps will be on the receiving-end of this operation. For example, the
built-in People app invokes the invite intent when the user selects "Add connection" for a specific
-social app that's listed in a person's contact details.</p>
+social app that's listed in a person's contact details.</p>
<p>To make your app visible as in the "Add connection" list, your app must provide a sync adapter to
sync contact information from your social network. You must then indicate to the system that your
@@ -327,7 +327,7 @@
function). You may provide multiple mipmap levels. If the output texture has not been bound to a
texture image, it will be automatically bound by the effect as a {@link
android.opengl.GLES20#GL_TEXTURE_2D} and with one mipmap level (0), which will have the same
-size as the input.</p>
+size as the input.</p>
<p>All effects listed in {@link android.media.effect.EffectFactory} are guaranteed to be supported.
However, some additional effects available from external libraries are not supported by all devices,
@@ -452,7 +452,7 @@
value is greater than zero. Also, some devices may not support identification of eyes and mouth,
in which case, those fields in the {@link android.hardware.Camera.Face} object will be null.</p>
-
+
<h4>Focus and metering areas</h4>
<p>Camera apps can now control the areas that the camera uses for focus and for metering white
@@ -495,7 +495,7 @@
<h4>Other camera features</h4>
-<ul>
+<ul>
<li>While recording video, you can now call {@link android.hardware.Camera#takePicture
takePicture()} to save a photo without interrupting the video session. Before doing so, you should
call {@link android.hardware.Camera.Parameters#isVideoSnapshotSupported} to be sure the hardware
@@ -775,7 +775,7 @@
<li>When invoked, the {@link
android.view.View#sendAccessibilityEvent sendAccessibilityEvent()} and {@link
android.view.View#sendAccessibilityEventUnchecked sendAccessibilityEventUnchecked()} methods defer
-to {@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}.
+to {@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}.
<p>Custom implementations of {@link android.view.View} might want to implement {@link
android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()} to
attach additional accessibility information to the {@link
@@ -1022,46 +1022,6 @@
-<h3 id="RenderScript">RenderScript</h3>
-
-<p>Three major features have been added to RenderScript:</p>
-
-<ul>
- <li>Off-screen rendering to a framebuffer object</li>
- <li>Rendering inside a view</li>
- <li>RS for each from the framework APIs</li>
-</ul>
-
-<p>The {@link android.renderscript.Allocation} class now supports a {@link
-android.renderscript.Allocation#USAGE_GRAPHICS_RENDER_TARGET} memory space, which allows you to
-render things directly into the {@link android.renderscript.Allocation} and use it as a framebuffer
-object.</p>
-
-<p>{@link android.renderscript.RSTextureView} provides a means to display RenderScript graphics
-inside of a {@link android.view.View}, unlike {@link android.renderscript.RSSurfaceView}, which
-creates a separate window. This key difference allows you to do things such as move, transform, or
-animate an {@link android.renderscript.RSTextureView} as well as draw RenderScript graphics inside
-a view that lies within an activity layout.</p>
-
-<p>The {@link android.renderscript.Script#forEach Script.forEach()} method allows you to call
-RenderScript compute scripts from the VM level and have them automatically delegated to available
-cores on the device. You do not use this method directly, but any compute RenderScript that you
-write will have a {@link android.renderscript.Script#forEach forEach()} method that you can call in
-the reflected RenderScript class. You can call the reflected {@link
-android.renderscript.Script#forEach forEach()} method by passing in an input {@link
-android.renderscript.Allocation} to process, an output {@link android.renderscript.Allocation} to
-write the result to, and a {@link android.renderscript.FieldPacker} data structure in case the
-RenderScript needs more information. Only one of the {@link android.renderscript.Allocation}s is
-necessary and the data structure is optional.</p>
-
-
-
-
-
-
-
-
-
<h3 id="Enterprise">Enterprise</h3>
<p>Android 4.0 expands the capabilities for enterprise application with the following features.</p>
@@ -1758,7 +1718,7 @@
notes for more information.</li>
</ul>
</dd>
-
+
<dt><a href="android-3.1.html">Android 3.1</a></dt>
<dd>
<ul>
@@ -1781,7 +1741,7 @@
notes for many more new APIs.</li>
</ul>
</dd>
-
+
<dt><a href="android-3.2.html">Android 3.2</a></dt>
<dd>
<ul>
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 @@
<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/design/patterns/widgets.jd b/docs/html/design/patterns/widgets.jd
index a5979ce..f2b0f4a 100644
--- a/docs/html/design/patterns/widgets.jd
+++ b/docs/html/design/patterns/widgets.jd
@@ -16,7 +16,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-6">
<h3>Collection widgets</h3>
- <p>As the name implies, collection widgets specialize on displaying multitude elements of the same type, such as a collection of pictures from a gallery app, a collection of articles from a news app or a collection of emails/messages from a communication app. Collection widgets typically focus on two use cases: browsing the collection, and opening an element of the collection to its detail view for consumption. Collection widgets can scroll vertically.</p>
+ <p>As the name implies, collection widgets specialize in displaying multitude elements of the same type, such as a collection of pictures from a gallery app, a collection of articles from a news app or a collection of emails/messages from a communication app. Collection widgets typically focus on two use cases: browsing the collection, and opening an element of the collection to its detail view for consumption. Collection widgets can scroll vertically.</p>
</div>
<div class="layout-content-col span-3">
<img src="{@docRoot}design/media/widgets_collection_gmail.png">
@@ -137,4 +137,4 @@
<li>Choose the right widget type for your purpose.</li>
<li>For resizable widgets, plan how the content for your widget should adapt to different sizes.</li>
<li>Make your widget orientation and device independent by ensuring that the layout is capable of stretching and contracting.</li>
-</ul>
\ No newline at end of file
+</ul>
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 @@
</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 @@
<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/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 @@
<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 @@
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 @@
</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/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 @@
<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 @@
</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 @@
<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 @@
</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 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 @@
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 @@
<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 @@
<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 @@
{@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 @@
<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 @@
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/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 @@
</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 @@
<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 @@
</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 @@
<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 @@
</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 @@
</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 @@
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 @@
<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 @@
<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 @@
</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 @@
<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/graphics/renderscript/graphics.jd b/docs/html/guide/topics/graphics/renderscript/graphics.jd
deleted file mode 100644
index 58676ea..0000000
--- a/docs/html/guide/topics/graphics/renderscript/graphics.jd
+++ /dev/null
@@ -1,994 +0,0 @@
-page.title=Graphics
-parent.title=Renderscript
-parent.link=index.html
-
-@jd:body
-
- <div id="qv-wrapper">
- <div id="qv">
- <h2>In this document</h2>
-
- <ol>
- <li>
- <a href="#creating-graphics-rs">Creating a Graphics Renderscript</a>
- <ol>
- <li><a href="#creating-native">Creating the Renderscript file</a></li>
- <li><a href="#creating-entry">Creating the Renderscript entry point class</a></li>
- <li><a href="#creating-view">Creating the view class</a></li>
- <li><a href="#creating-activity">Creating the activity class</a></li>
- </ol>
- </li>
- <li>
- <a href="#drawing">Drawing</a>
- <ol>
- <li><a href="#drawing-rsg">Simple drawing</a></li>
- <li><a href="#drawing-mesh">Drawing with a mesh</a></li>
- </ol>
- </li>
- <li>
- <a href="#shaders">Shaders</a>
- <ol>
- <li><a href="#shader-bindings">Shader bindings</a></li>
- <li><a href="#shader-sampler">Defining a sampler</a></li>
- </ol>
- </li>
- <li>
- <a href="#fbo">Rendering to a Framebuffer Object</a>
- </li>
- </ol>
-
- <h2>Related Samples</h2>
-
- <ol>
- <li><a href="{@docRoot}resources/samples/RenderScript/Balls/index.html">Balls</a></li>
-
- <li><a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Fountain</a></li>
-
- <li><a href="{@docRoot}resources/samples/RenderScript/FountainFbo/index.html">FountainFbo</a></li>
-
- <li><a href="{@docRoot}resources/samples/RenderScript/HelloWorld/index.html">Hello
-World</a></li>
-
- <li><a
-href="{@docRoot}resources/samples/RenderScript/MiscSamples/index.html">Misc Samples</a></li>
- </ol>
- </div>
- </div>
-
- <p>Renderscript provides a number of graphics APIs for rendering, both at the Android
- framework level as well as at the Renderscript runtime level. For instance, the Android framework APIs let you
- create meshes and define shaders to customize the graphical rendering pipeline. The native
- Renderscript graphics APIs let you draw the actual meshes to render your scene. You need to
- be familiar with both APIs to appropriately render graphics on an Android-powered device.</p>
-
- <h2 id="creating-graphics-rs">Creating a Graphics Renderscript</h2>
-
- <p>Renderscript applications require various layers of code, so it is useful to create the following
- files to help keep your application organized:</p>
-
- <dl>
- <dt>The Renderscript <code>.rs</code> file</dt>
-
- <dd>This file contains the logic to do the graphics rendering.</dd>
-
- <dt>The Renderscript entry point <code>.java</code> class</dt>
-
- <dd>This class allows the view class to interact with the code defined in the <code>.rs</code>
- file. This class contains a Renderscript object (instance of
- <code>ScriptC_<em>renderscript_file</em></code>), which allows your Android framework code to
- call the Renderscript code. In general, this class does much of the setup for Renderscript
- such as shader and mesh building and memory allocation and binding. The SDK samples follow the
- convention of naming this file ActivityRS.java,
- where Activity is the name of your main activity class.</dd>
-
- <dt>The view <code>.java</code> class</dt>
-
- <dd>This class extends {@link android.renderscript.RSSurfaceView} or {@link
- android.renderscript.RSTextureView} to provide a surface to render on. A {@link
- android.renderscript.RSSurfaceView} consumes a whole window, but a {@link
- android.renderscript.RSTextureView} allows you to draw Renderscript graphics inside of a
- view and add it to a {@link android.view.ViewGroup} alongside
- other views. In this class, you create a {@link android.renderscript.RenderScriptGL} context object
- with a call to {@link android.renderscript.RSSurfaceView#createRenderScriptGL
- RSSurfaceView.createRenderscriptGL()} or {@link android.renderscript.RSTextureView#createRenderScriptGL
- RSTextureView.createRenderscriptGL()}. The {@link android.renderscript.RenderScriptGL} context object
- contains information about the current rendering state of Renderscript such as the vertex and
- fragment shaders. You pass this context object to the Renderscript entry point class, so that
- class can modify the rendering context if needed and bind the Renderscript code to the context. Once bound,
- the view class can use the Renderscript code to display graphics.
- The view class should also implement callbacks for events inherited from {@link
- android.view.View}, such as {@link android.view.View#onTouchEvent onTouchEvent()} and {@link
- android.view.View#onKeyDown onKeyDown()} if you want to detect these types of user interactions.
- The SDK samples follow the convention of naming this file ActivityView.java,
- where Activity is the name of your main activity class</dd>
-
- <dt>The activity <code>.java</code> class</dt>
-
- <dd>This class is the main activity class and sets your {@link android.renderscript.RSSurfaceView} as the main content
- view for this activity or uses the {@link android.renderscript.RSTextureView} alongside other views.</dd>
- </dl>
- <p>Figure 1 describes how these classes interact with one another in a graphics Renderscript:</p>
-
- <img src="{@docRoot}images/rs_graphics.png">
- <p class="img-caption"><strong>Figure 1.</strong> Graphics Renderscript overview</p>
-
-
- <p>The following sections describe how to create an application that uses a graphics Renderscript by using
- the <a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Renderscript Fountain
- sample</a> that is provided in the SDK as a guide (some code has been modified from its original
- form for simplicity).</p>
-
- <h3 id="creating-native">Creating the Renderscript file</h3>
-
- <p>Your Renderscript code resides in <code>.rs</code> and <code>.rsh</code> (headers) files in the
- <code><project_root>/src/</code> directory. This code contains the logic to render your
- graphics and declares all other necessary items such as variables, structs,
- and pointers. Every graphics <code>.rs</code> file generally contains the following items:</p>
-
- <ul>
- <li>A pragma declaration (<code>#pragma rs java_package_name(<em>package.name</em>)</code>) that declares
- the package name of the <code>.java</code> reflection of this Renderscript.</li>
-
- <li>A pragma declaration (<code>#pragma version(1)</code>) that declares the version of Renderscript that
- you are using (1 is the only value for now).</li>
-
- <li>A <code>#include "rs_graphics.rsh"</code> declaration.</li>
-
- <li>A <code>root()</code> function. This is the main worker function for your Renderscript and
- calls Renderscript graphics functions to render scenes. This function is called every time a
- frame refresh occurs, which is specified as its return value. A <code>0</code> (zero) specified for
- the return value says to only render the frame when a property of the scene that you are
- rendering changes. A non-zero positive integer specifies the refresh rate of the frame in
- milliseconds.
-
- <p class="note"><strong>Note:</strong> The Renderscript runtime makes its best effort to
- refresh the frame at the specified rate. For example, if you are creating a live wallpaper
- and set the return value to 20, the Renderscript runtime renders the wallpaper at 50fps if it has just
- enough or more resources to do so. It renders as fast as it can if not enough resources
- are available.</p>
-
- <p>For more information on using the Renderscript graphics functions, see the <a href=
- "#drawing">Drawing</a> section.</p>
- </li>
-
- <li>An <code>init()</code> function. This allows you to do initialization of your
- Renderscript before the <code>root()</code> function runs, such as assigning values to variables. This
- function runs once and is called automatically when the Renderscript starts, before anything
- else in your Renderscript. Creating this function is optional.</li>
-
- <li>Any variables, pointers, and structures that you wish to use in your Renderscript code (can
- be declared in <code>.rsh</code> files if desired)</li>
- </ul>
-
- <p>The following code shows how the <code>fountain.rs</code> file is implemented:</p>
- <pre>
-#pragma version(1)
-
-// Tell which java package name the reflected files should belong to
-#pragma rs java_package_name(com.example.android.rs.fountain)
-
-//declare shader binding
-#pragma stateFragment(parent)
-
-// header with graphics APIs, must include explicitly
-#include "rs_graphics.rsh"
-
-static int newPart = 0;
-
-// the mesh to render
-rs_mesh partMesh;
-
-// the point representing where a particle is rendered
-typedef struct __attribute__((packed, aligned(4))) Point {
- float2 delta;
- float2 position;
- uchar4 color;
-} Point_t;
-Point_t *point;
-
-// main worker function that renders particles onto the screen
-int root() {
- float dt = min(rsGetDt(), 0.1f);
- rsgClearColor(0.f, 0.f, 0.f, 1.f);
- const float height = rsgGetHeight();
- const int size = rsAllocationGetDimX(rsGetAllocation(point));
- float dy2 = dt * (10.f);
- Point_t * p = point;
- for (int ct=0; ct < size; ct++) {
- p->delta.y += dy2;
- p->position += p->delta;
- if ((p->position.y > height) && (p->delta.y > 0)) {
- p->delta.y *= -0.3f;
- }
- p++;
- }
-
- rsgDrawMesh(partMesh);
- return 1;
-}
-
-// adds particles to the screen to render
-static float4 partColor[10];
-void addParticles(int rate, float x, float y, int index, bool newColor)
-{
- if (newColor) {
- partColor[index].x = rsRand(0.5f, 1.0f);
- partColor[index].y = rsRand(1.0f);
- partColor[index].z = rsRand(1.0f);
- }
- float rMax = ((float)rate) * 0.02f;
- int size = rsAllocationGetDimX(rsGetAllocation(point));
- uchar4 c = rsPackColorTo8888(partColor[index]);
-
- Point_t * np = &point[newPart];
- float2 p = {x, y};
- while (rate--) {
- float angle = rsRand(3.14f * 2.f);
- float len = rsRand(rMax);
- np->delta.x = len * sin(angle);
- np->delta.y = len * cos(angle);
- np->position = p;
- np->color = c;
- newPart++;
- np++;
- if (newPart >= size) {
- newPart = 0;
- np = &point[newPart];
- }
- }
-}
-</pre>
-
- <h3 id="creating-entry">Creating the Renderscript entry point class</h3>
-
- <p>When you create a Renderscript (<code>.rs</code>) file, it is helpful to create a
- corresponding Android framework class that is an entry point into the <code>.rs</code> file.
- The most important thing this class does is receive a {@link android.renderscript.RenderScriptGL} rendering context
- object from the <a href="#creating-view">view class</a> and binds the actual Renderscript
- code to the rendering context. This notifies your view class of the code that it needs
- to render graphics.
- </p>
-
- <p>In addition, this class should contain all of the things needed to set up Renderscript.
- Some important things that you need to do in this class are:</p>
-
- <ul>
- <li>Create a Renderscript object
- <code>ScriptC_<em>rs_filename</em></code>. The Renderscript object is attached to the Renderscript bytecode, which is platform-independent and
- gets compiled on the device when the Renderscript application runs. The bytecode is referenced
- as a raw resource and is passed into the constructor for the Renderscript object.
- For example, this is how the <a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Fountain</a>
- sample creates the Renderscript object:
- <pre>
- RenderScriptGL rs; //obtained from the view class
- Resources res; //obtained from the view class
- ...
- ScriptC_fountain mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain);
- </pre>
- </li>
- <li>Allocate any necessary memory and bind it to your Renderscript code via the Renderscript object.</li>
- <li>Build any necessary meshes and bind them to the Renderscript code via the Renderscript object.</li>
- <li>Create any necessary programs and bind them to the Renderscript code via the Renderscript object.</li>
- </ul>
-
- <p>The following code shows how the <a href=
- "{@docRoot}resources/samples/RenderScript/Fountain/src/com/example/android/rs/fountain/FountainRS.html">
- FountainRS</a> class is implemented:</p>
- <pre>
-package com.example.android.rs.fountain;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.Log;
-
-public class FountainRS {
- public static final int PART_COUNT = 50000;
-
- public FountainRS() {
- }
-
- /**
- * This provides us with the Renderscript context and resources
- * that allow us to create the Renderscript object
- */
- private Resources mRes;
- private RenderScriptGL mRS;
-
- // Renderscript object
- private ScriptC_fountain mScript;
-
- // Called by the view class to initialize the Renderscript context and renderer
- public void init(RenderScriptGL rs, Resources res) {
- mRS = rs;
- mRes = res;
-
- /**
- * Create a shader and bind to the Renderscript context
- */
- ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
- pfb.setVaryingColor(true);
- rs.bindProgramFragment(pfb.create());
-
- /**
- * Allocate memory for the particles to render and create the mesh to draw
- */
- ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);
- Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
- smb.addVertexAllocation(points.getAllocation());
- smb.addIndexSetType(Mesh.Primitive.POINT);
- Mesh sm = smb.create();
-
- /**
- * Create and bind the Renderscript object to the Renderscript context
- */
- mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain);
- mScript.set_partMesh(sm);
- mScript.bind_point(points);
- mRS.bindRootScript(mScript);
- }
-
- boolean holdingColor[] = new boolean[10];
-
- /**
- * Calls Renderscript functions (invoke_addParticles)
- * via the Renderscript object to add particles to render
- * based on where a user touches the screen.
- */
- public void newTouchPosition(float x, float y, float pressure, int id) {
- if (id >= holdingColor.length) {
- return;
- }
- int rate = (int)(pressure * pressure * 500.f);
- if (rate > 500) {
- rate = 500;
- }
- if (rate > 0) {
- mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]);
- holdingColor[id] = true;
- } else {
- holdingColor[id] = false;
- }
-
- }
-}
-</pre>
-
-
- <h3 id="creating-view">Creating the view class</h3>
-
-
- <p>To display graphics, you need a view to render on. Create a class that extends {@link
- android.renderscript.RSSurfaceView} or {@link android.renderscript.RSTextureView}. This class
- allows you to create a {@link android.renderscript.RenderScriptGL} context object by calling and
- pass it to the Rendscript entry point class to bind the two. Once bound, the content is aware
- of the code that it needs to use to render graphics with. If your Renderscript code
- depends on any type of information that the view is aware of, such as touches from the user,
- you can also use this class to relay that information to the Renderscript entry point class.
- The following code shows how the <code>FountainView</code> class is implemented:</p>
- <pre>
-package com.example.android.rs.fountain;
-
-import android.renderscript.RSTextureView;
-import android.renderscript.RenderScriptGL;
-import android.content.Context;
-import android.view.MotionEvent;
-
-public class FountainView extends RSTextureView {
-
- public FountainView(Context context) {
- super(context);
- }
- // Renderscript context
- private RenderScriptGL mRS;
- // Renderscript entry point object that calls Renderscript code
- private FountainRS mRender;
-
- /**
- * Create Renderscript context and initialize Renderscript entry point
- */
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- android.util.Log.e("rs", "onAttachedToWindow");
- if (mRS == null) {
- RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
- mRS = createRenderScriptGL(sc);
- mRender = new FountainRS();
- mRender.init(mRS, getResources());
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- android.util.Log.e("rs", "onDetachedFromWindow");
- if (mRS != null) {
- mRS = null;
- destroyRenderScriptGL();
- }
- }
-
-
- /**
- * Use callbacks to relay data to Renderscript entry point class
- */
- @Override
- public boolean onTouchEvent(MotionEvent ev)
- {
- int act = ev.getActionMasked();
- if (act == ev.ACTION_UP) {
- mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
- return false;
- } else if (act == MotionEvent.ACTION_POINTER_UP) {
- // only one pointer going up, we can get the index like this
- int pointerIndex = ev.getActionIndex();
- int pointerId = ev.getPointerId(pointerIndex);
- mRender.newTouchPosition(0, 0, 0, pointerId);
- }
- int count = ev.getHistorySize();
- int pcount = ev.getPointerCount();
-
- for (int p=0; p < pcount; p++) {
- int id = ev.getPointerId(p);
- mRender.newTouchPosition(ev.getX(p),
- ev.getY(p),
- ev.getPressure(p),
- id);
-
- for (int i=0; i < count; i++) {
- mRender.newTouchPosition(ev.getHistoricalX(p, i),
- ev.getHistoricalY(p, i),
- ev.getHistoricalPressure(p, i),
- id);
- }
- }
- return true;
- }
-}
-</pre>
-
- <h3 id="creating-activity">Creating the activity class</h3>
-
- <p>Applications that use Renderscript still behave like normal Android applications, so you
- need an activity class that handles activity lifecycle callback events appropriately. The activity class
- also sets your {@link android.renderscript.RSSurfaceView} view class to be the main content view of the
- activity or uses your {@link android.renderscript.RSTextureView}
- in a {@link android.view.ViewGroup} alongside other views.</p>
-
- <p>The following code shows how the <a href="{@docRoot}resources/samples/RenderScript/Fountain/index.html">Fountain</a>
- sample declares its activity class:</p>
- <pre>
-package com.example.android.rs.fountain;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-public class Fountain extends Activity {
-
- private static final String LOG_TAG = "libRS_jni";
- private static final boolean DEBUG = false;
- private static final boolean LOG_ENABLED = false;
-
- private FountainView mView;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- // Create our Preview view and set it as
- // the content of our activity
- mView = new FountainView(this);
- setContentView(mView);
- }
-
- @Override
- protected void onResume() {
- Log.e("rs", "onResume");
-
- // Ideally a game should implement onResume() and onPause()
- // to take appropriate action when the activity looses focus
- super.onResume();
- mView.resume();
- }
-
- @Override
- protected void onPause() {
- Log.e("rs", "onPause");
-
- // Ideally a game should implement onResume() and onPause()
- // to take appropriate action when the activity looses focus
- super.onPause();
- mView.pause();
-
- }
-
- static void log(String message) {
- if (LOG_ENABLED) {
- Log.v(LOG_TAG, message);
- }
- }
-}
-</pre>
-
-<p>Now that you have an idea of what is involved in a Renderscript graphics application, you can
-start building your own. It might be easiest to begin with one of the
-<a href="{@docRoot}resources/samples/RenderScript/index.html">Renderscript samples</a> as a starting
-point if this is your first time using Renderscript.</p>
-
- <h2 id="drawing">Drawing</h2>
- <p>The following sections describe how to use the graphics functions to draw with Renderscript.</p>
-
- <h3 id="drawing-rsg">Simple drawing</h3>
-
- <p>The native Renderscript APIs provide a few convenient functions to easily draw a polygon or text to
- the screen. You call these in your <code>root()</code> function to have them render to the {@link
- android.renderscript.RSSurfaceView} or {@link android.renderscript.RSTextureView}. These functions are
- available for simple drawing and should not be used for complex graphics rendering:</p>
-
- <ul>
- <li><code>rsgDrawRect()</code>: Sets up a mesh and draws a rectangle to the screen. It uses the
- top left vertex and bottom right vertex of the rectangle to draw.</li>
-
- <li><code>rsgDrawQuad()</code>: Sets up a mesh and draws a quadrilateral to the screen.</li>
-
- <li><code>rsgDrawQuadTexCoords()</code>: Sets up a mesh and draws a quadrilateral to the screen
- using the provided coordinates of a texture.</li>
-
- <li><code>rsgDrawText()</code>: Draws specified text to the screen. Use <code>rsgFontColor()</code>
- to set the color of the text.</li>
- </ul>
-
- <h3 id="drawing-mesh">Drawing with a mesh</h3>
-
- <p>When you want to render complex scenes to the screen, instantiate a {@link
- android.renderscript.Mesh} and draw it with <code>rsgDrawMesh()</code>. A {@link
- android.renderscript.Mesh} is a collection of allocations that represent vertex data (positions,
- normals, texture coordinates) and index data that provides information on how to draw triangles
- and lines with the provided vertex data. You can build a Mesh in three different ways:</p>
-
- <ul>
- <li>Build the mesh with the {@link android.renderscript.Mesh.TriangleMeshBuilder} class, which
- allows you to specify a set of vertices and indices for each triangle that you want to draw.</li>
-
- <li>Build the mesh using an {@link android.renderscript.Allocation} or a set of {@link
- android.renderscript.Allocation}s with the {@link android.renderscript.Mesh.AllocationBuilder}
- class. This approach allows you to build a mesh with vertices already stored in memory, which allows you
- to specify the vertices in Renderscript or Android framework code.</li>
-
- <li>Build the mesh with the {@link android.renderscript.Mesh.Builder} class. You should use
- this convenience method when you know the data types you want to use to build your mesh, but
- don't want to make separate memory allocations like with {@link
- android.renderscript.Mesh.AllocationBuilder}. You can specify the types that you want and this
- mesh builder automatically creates the memory allocations for you.</li>
- </ul>
-
- <p>To create a mesh using the {@link android.renderscript.Mesh.TriangleMeshBuilder}, you need to
- supply it with a set of vertices and the indices for the vertices that comprise the triangle. For
- example, the following code specifies three vertices, which are added to an internal array,
- indexed in the order they were added. The call to {@link
- android.renderscript.Mesh.TriangleMeshBuilder#addTriangle addTriangle()} draws the triangle with
- vertex 0, 1, and 2 (the vertices are drawn counter-clockwise).</p>
- <pre>
-int float2VtxSize = 2;
-Mesh.TriangleMeshBuilder triangles = new Mesh.TriangleMeshBuilder(renderscriptGL,
-float2VtxSize, Mesh.TriangleMeshBuilder.COLOR);
-triangles.addVertex(300.f, 300.f);
-triangles.addVertex(150.f, 450.f);
-triangles.addVertex(450.f, 450.f);
-triangles.addTriangle(0 , 1, 2);
-Mesh smP = triangle.create(true);
-script.set_mesh(smP);
-</pre>
-
- <p>To draw a mesh using the {@link android.renderscript.Mesh.AllocationBuilder}, you need to
- supply it with one or more allocations that contain the vertex data:</p>
- <pre>
-Allocation vertices;
-
-...
-Mesh.AllocationBuilder triangle = new Mesh.AllocationBuilder(mRS);
-smb.addVertexAllocation(vertices.getAllocation());
-smb.addIndexSetType(Mesh.Primitive.TRIANGLE);
-Mesh smP = smb.create();
-script.set_mesh(smP);
-</pre>
-
- <p>In your Renderscript code, draw the built mesh to the screen:</p>
- <pre>
-rs_mesh mesh;
-...
-
-int root(){
-...
-rsgDrawMesh(mesh);
-...
-return 0; //specify a non zero, positive integer to specify the frame refresh.
- //0 refreshes the frame only when the mesh changes.
-}
-</pre>
-
- <h2 id="shader">Programs</h2>
-
- <p>You can attach four program objects to the {@link android.renderscript.RenderScriptGL} context
- to customize the rendering pipeline. For example, you can create vertex and fragment shaders in
- GLSL or build a raster program object that controls culling. The four programs mirror a
- traditional graphical rendering pipeline:</p>
-
- <table>
- <tr>
- <th>Android Object Type</th>
-
- <th>Renderscript Native Type</th>
-
- <th>Description</th>
- </tr>
-
- <tr>
- <td>{@link android.renderscript.ProgramVertex}</td>
-
- <td>rs_program_vertex</td>
-
- <td>
- <p>The Renderscript vertex program, also known as a vertex shader, describes the stage in
- the graphics pipeline responsible for manipulating geometric data in a user-defined way.
- The object is constructed by providing Renderscript with the following data:</p>
-
- <ul>
- <li>An {@link android.renderscript.Element} describing its varying inputs or attributes</li>
-
- <li>GLSL shader string that defines the body of the program</li>
-
- <li>a {@link android.renderscript.Type} that describes the layout of an
- Allocation containing constant or uniform inputs</li>
- </ul>
-
- <p>Once the program is created, bind it to the {@link android.renderscript.RenderScriptGL}
- graphics context by calling {@link android.renderscript.RenderScriptGL#bindProgramVertex
- bindProgramVertex()}. It is then used for all subsequent draw calls until you bind a new
- program. If the program has constant inputs, the user needs to bind an allocation
- containing those inputs. The allocation's type must match the one provided during creation.
- </p>
-
- <p>The Renderscript runtime then does all the necessary plumbing to send those constants to
- the graphics hardware. Varying inputs to the shader, such as position, normal, and texture
- coordinates are matched by name between the input {@link android.renderscript.Element}
- and the mesh object that is being drawn. The signatures don't have to be exact or in any
- strict order. As long as the input name in the shader matches a channel name and size
- available on the mesh, the Renderscript runtime handles connecting the two. Unlike OpenGL
- there is no need to link the vertex and fragment programs.</p>
-
- <p>To bind shader constants to the program, declare a <code>struct</code> that contains the necessary
- shader constants in your Renderscript code. This <code>struct</code> is generated into a
- reflected class that you can use as a constant input element during the program's creation.
- It is an easy way to create an instance of this <code>struct</code> as an allocation. You would then
- bind this {@link android.renderscript.Allocation} to the program and the
- Renderscript runtime sends the data that is contained in the <code>struct</code> to the hardware
- when necessary. To update shader constants, you change the values in the
- {@link android.renderscript.Allocation} and notify the Renderscript
- code of the change.</p>
-
- <p>The {@link android.renderscript.ProgramVertexFixedFunction.Builder} class also
- lets you build a simple vertex shader without writing GLSL code.
- </p>
- </td>
- </tr>
-
- <tr>
- <td>{@link android.renderscript.ProgramFragment}</td>
-
- <td>rs_program_fragment</td>
-
- <td>
- <p>The Renderscript fragment program, also known as a fragment shader, is responsible for
- manipulating pixel data in a user-defined way. It's constructed from a GLSL shader string
- containing the program body, texture inputs, and a {@link android.renderscript.Type}
- object that describes the constants
- used by the program. Like the vertex programs, when an {@link android.renderscript.Allocation}
- with constant input
- values is bound to the shader, its values are sent to the graphics program automatically.
- Note that the values inside the {@link android.renderscript.Allocation} are not explicitly tracked.
- If they change between two draw calls using the same program object, notify the runtime of that change by
- calling <code>rsgAllocationSyncAll()</code>, so it can send the new values to hardware. Communication
- between the vertex and fragment programs is handled internally in the GLSL code. For
- example, if the fragment program is expecting a varying input called <code>varTex0</code>, the GLSL code
- inside the program vertex must provide it.</p>
-
- <p>To bind shader constructs to the program, declare a <code>struct</code> that contains the necessary
- shader constants in your Renderscript code. This <code>struct</code> is generated into a
- reflected class that you can use as a constant input element during the program's creation.
- It is an easy way to create an instance of this <code>struct</code> as an allocation. You would then
- bind this {@link android.renderscript.Allocation} to the program and the
- Renderscript runtime sends the data that is contained in the <code>struct</code> to the hardware
- when necessary. To update shader constants, you change the values in the
- {@link android.renderscript.Allocation} and notify the Renderscript
- code of the change.</p>
-
- <p>The {@link android.renderscript.ProgramFragmentFixedFunction.Builder} class also
- lets you build a simple fragment shader without writing GLSL code.
- </p>
- </td>
- </tr>
-
- <tr>
- <td>{@link android.renderscript.ProgramStore}</td>
-
- <td>rs_program_store</td>
-
- <td>The Renderscript store program contains a set of parameters that control how the graphics
- hardware writes to the framebuffer. It could be used to enable and disable depth writes and
- testing, setup various blending modes for effects like transparency and define write masks
- for color components.</td>
- </tr>
-
- <tr>
- <td>{@link android.renderscript.ProgramRaster}</td>
-
- <td>rs_program_raster</td>
-
- <td>The Renderscript raster program is primarily used to specify whether point sprites are enabled and to
- control the culling mode. By default back faces are culled.</td>
- </tr>
- </table>
-
- <p>The following example defines a vertex shader in GLSL and binds it to a Renderscript context object:</p>
- <pre>
- private RenderScriptGL glRenderer; //rendering context
- private ScriptField_Point mPoints; //vertices
- private ScriptField_VpConsts mVpConsts; //shader constants
-
- ...
-
- ProgramVertex.Builder sb = new ProgramVertex.Builder(glRenderer);
- String t = "varying vec4 varColor;\n" +
- "void main() {\n" +
- " vec4 pos = vec4(0.0, 0.0, 0.0, 1.0);\n" +
- " pos.xy = ATTRIB_position;\n" +
- " gl_Position = UNI_MVP * pos;\n" +
- " varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" +
- " gl_PointSize = ATTRIB_size;\n" +
- "}\n";
- sb.setShader(t);
- sb.addConstant(mVpConsts.getType());
- sb.addInput(mPoints.getElement());
- ProgramVertex pvs = sb.create();
- pvs.bindConstants(mVpConsts.getAllocation(), 0);
- glRenderer.bindProgramVertex(pvs);
-</pre>
-
-
- <p>The <a href=
- "{@docRoot}resources/samples/RenderScript/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.html">
- RsRenderStatesRS</a> sample has many examples on how to create a shader without writing GLSL.</p>
-
- <h3 id="shader-bindings">Program bindings</h3>
-
- <p>You can also declare four pragmas that control default program bindings to the {@link
- android.renderscript.RenderScriptGL} context when the script is executing:</p>
-
- <ul>
- <li><code>stateVertex</code></li>
-
- <li><code>stateFragment</code></li>
-
- <li><code>stateRaster</code></li>
-
- <li><code>stateStore</code></li>
- </ul>
-
- <p>The possible values for each pragma are <code>parent</code> or <code>default</code>. Using
- <code>default</code> binds the shaders to the graphical context with the system defaults.</p>
-
- <p>Using <code>parent</code> binds the shaders in the same manner as it is bound in the calling
- script. If this is the root script, the parent state is taken from the bind points that are set
- by the {@link android.renderscript.RenderScriptGL} bind methods.</p>
-
- <p>For example, you can define this at the top of your graphics Renderscript code to have
- the vertex and store programs inherent the bind properties from their parent scripts:</p>
- <pre>
-#pragma stateVertex(parent)
-#pragma stateStore(parent)
-</pre>
-
- <h3 id="shader-sampler">Defining a sampler</h3>
-
- <p>A {@link android.renderscript.Sampler} object defines how data is extracted from textures.
- Samplers are bound to a {@link android.renderscript.ProgramFragment} alongside the texture
- whose sampling they control. These
- objects are used to specify such things as edge clamping behavior, whether mip-maps are used, and
- the amount of anisotropy required. There might be situations where hardware does not support the
- desired behavior of the sampler. In these cases, the Renderscript runtime attempts to provide the
- closest possible approximation. For example, the user requested 16x anisotropy, but only 8x was
- set because it's the best available on the hardware.</p>
-
- <p>The <a href=
- "{@docRoot}resources/samples/RenderScript/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.html">
- RsRenderStatesRS</a> sample has many examples on how to create a sampler and bind it to a
- Fragment program.</p>
-
-
-
-<h2 id="fbo">Rendering to a Framebuffer Object</h2>
-
-<p>Framebuffer objects allow you to render offscreen instead of in the default onscreen
-framebuffer. This approach might be useful for situations where you need to post-process a texture before
-rendering it to the screen, or when you want to composite two scenes in one such as rendering a rear-view
-mirror of a car. There are two buffers associated with a framebuffer object: a color buffer
-and a depth buffer. The color buffer (required) contains the actual pixel data of the scene
-that you are rendering, and the depth buffer (optional) contains the values necessary to figure
-out what vertices are drawn depending on their z-values.</p>
-
-<p>In general, you need to do the following to render to a framebuffer object:</p>
-
-<ul>
- <li>Create {@link android.renderscript.Allocation} objects for the color buffer and
- depth buffer (if needed). Specify the {@link
- android.renderscript.Allocation#USAGE_GRAPHICS_RENDER_TARGET} usage attribute for these
- allocations to notify the Renderscript runtime to use these allocations for the framebuffer
- object. For the color buffer allocation, you most likely need to declare the {@link
- android.renderscript.Allocation#USAGE_GRAPHICS_TEXTURE} usage attribute
- to use the color buffer as a texture, which is the most common use of the framebuffer object.</li>
-
- <li>Tell the Renderscript runtime to render to the framebuffer object instead of the default
- framebuffer by calling <code>rsgBindColorTarget()</code> and passing it the color buffer
- allocation. If applicable, call <code>rsgBindDepthTarget()</code> passing in the depth buffer
- allocation as well.</li>
-
- <li>Render your scene normally with the <code>rsgDraw</code> functions. The scene will be
- rendered into the color buffer instead of the default onscreen framebuffer.</li>
-
- <li>When done, tell the Renderscript runtime stop rendering to the color buffer and back
- to the default framebuffer by calling <code>rsgClearAllRenderTargets()</code>.</li>
-
- <li>Create a fragment shader and bind a the color buffer to it as a texture.</li>
-
- <li>Render your scene to the default framebuffer. The texture will be used according
- to the way you setup your fragment shader.</li>
-</ul>
-
-<p>The following example shows you how to render to a framebuffer object by modifying the
-<a href="{@docRoot}guide/resources/renderscript/Fountain/">Fountain</a> Renderscript sample. The end
-result is the <a href="{@docRoot}guide/resources/renderscript/FountainFBO/">FountainFBO</a> sample.
-The modifications render the exact same scene into a framebuffer object as it does the default
-framebuffer. The framebuffer object is then rendered into the default framebuffer in a small
-area at the top left corner of the screen.</p>
-
-<ol>
- <li>Modify <code>fountain.rs</code> and add the following global variables. This creates setter
- methods when this file is reflected into a <code>.java</code> file, allowing you to allocate
- memory in your Android framework code and binding it to the Renderscript runtime.
-<pre>
-//allocation for color buffer
-rs_allocation gColorBuffer;
-//fragment shader for rendering without a texture (used for rendering to framebuffer object)
-rs_program_fragment gProgramFragment;
-//fragment shader for rendering with a texture (used for rendering to default framebuffer)
-rs_program_fragment gTextureProgramFragment;
-</pre>
- </li>
-
- <li>Modify the root function of <code>fountain.rs</code> to look like the following code. The
- modifications are commented:
-<pre>
-int root() {
- float dt = min(rsGetDt(), 0.1f);
- rsgClearColor(0.f, 0.f, 0.f, 1.f);
- const float height = rsgGetHeight();
- const int size = rsAllocationGetDimX(rsGetAllocation(point));
- float dy2 = dt * (10.f);
- Point_t * p = point;
- for (int ct=0; ct < size; ct++) {
- p->delta.y += dy2;
- p->position += p->delta;
- if ((p->position.y > height) && (p->delta.y > 0)) {
- p->delta.y *= -0.3f;
- }
- p++;
- }
- //Tell Renderscript runtime to render to the frame buffer object
- rsgBindColorTarget(gColorBuffer, 0);
- //Begin rendering on a white background
- rsgClearColor(1.f, 1.f, 1.f, 1.f);
- rsgDrawMesh(partMesh);
-
- //When done, tell Renderscript runtime to stop rendering to framebuffer object
- rsgClearAllRenderTargets();
-
- //Bind a new fragment shader that declares the framebuffer object to be used as a texture
- rsgBindProgramFragment(gTextureProgramFragment);
-
- //Bind the framebuffer object to the fragment shader at slot 0 as a texture
- rsgBindTexture(gTextureProgramFragment, 0, gColorBuffer);
- //Draw a quad using the framebuffer object as the texture
- float startX = 10, startY = 10;
- float s = 256;
- rsgDrawQuadTexCoords(startX, startY, 0, 0, 1,
- startX, startY + s, 0, 0, 0,
- startX + s, startY + s, 0, 1, 0,
- startX + s, startY, 0, 1, 1);
-
- //Rebind the original fragment shader to render as normal
- rsgBindProgramFragment(gProgramFragment);
-
- //Render the main scene
- rsgDrawMesh(partMesh);
-
- return 1;
-}
-</pre>
- </li>
-
- <li>In the <code>FountainRS.java</code> file, modify the <code>init()</code> method to look
- like the following code. The modifications are commented:
-
-<pre>
-/* Add necessary members */
-private ScriptC_fountainfbo mScript;
-private Allocation mColorBuffer;
-private ProgramFragment mProgramFragment;
-private ProgramFragment mTextureProgramFragment;
-
-public void init(RenderScriptGL rs, Resources res) {
- mRS = rs;
- mRes = res;
-
- ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);
-
- Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
- smb.addVertexAllocation(points.getAllocation());
- smb.addIndexSetType(Mesh.Primitive.POINT);
- Mesh sm = smb.create();
-
- mScript = new ScriptC_fountainfbo(mRS, mRes, R.raw.fountainfbo);
- mScript.set_partMesh(sm);
- mScript.bind_point(points);
-
- ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
- pfb.setVaryingColor(true);
- mProgramFragment = pfb.create();
- mScript.set_gProgramFragment(mProgramFragment);
-
- /* Second fragment shader to use a texture (framebuffer object) to draw with */
- pfb.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
- ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-
- /* Set the fragment shader in the Renderscript runtime */
- mTextureProgramFragment = pfb.create();
- mScript.set_gTextureProgramFragment(mTextureProgramFragment);
-
- /* Create the allocation for the color buffer */
- Type.Builder colorBuilder = new Type.Builder(mRS, Element.RGBA_8888(mRS));
- colorBuilder.setX(256).setY(256);
- mColorBuffer = Allocation.createTyped(mRS, colorBuilder.create(),
- Allocation.USAGE_GRAPHICS_TEXTURE |
- Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-
- /* Set the allocation in the Renderscript runtime */
- mScript.set_gColorBuffer(mColorBuffer);
-
- mRS.bindRootScript(mScript);
-}
-</pre>
-
-<p class="note"><strong>Note:</strong> This sample doesn't use a depth buffer, but the following code
-shows you how to declare an example depth buffer if you need to use
-one for your application. The depth buffer must have the same dimensions as the color buffer:
-
-<pre>
-Allocation mDepthBuffer;
-
-...
-
-Type.Builder b = new Type.Builder(mRS, Element.createPixel(mRS, DataType.UNSIGNED_16,
- DataKind.PIXEL_DEPTH));
-b.setX(256).setY(256);
-mDepthBuffer = Allocation.createTyped(mRS, b.create(),
-Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-
-</pre>
-</p>
-</li>
-
- <li>Run and use the sample. The smaller, white quad on the top-left corner is using the
- framebuffer object as a texture, which renders the same scene as the main rendering.</li>
-</ol>
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 @@
<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 @@
<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 @@
<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 @@
<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 @@
</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/manifest/meta-data-element.jd b/docs/html/guide/topics/manifest/meta-data-element.jd
index 85a871d..56a214c 100644
--- a/docs/html/guide/topics/manifest/meta-data-element.jd
+++ b/docs/html/guide/topics/manifest/meta-data-element.jd
@@ -80,7 +80,7 @@
</tr><tr>
<td>Color value, in the form "{@code #rgb}", "{@code #argb}",
"{@code #rrggbb}", or "{@code #aarrggbb}"</td>
- <td>{@link android.os.Bundle#getString(String) getString()}</td>
+ <td>{@link android.os.Bundle#getInt(String) getInt()}</td>
</tr><tr>
<td>Float value, such as "{@code 1.23}"</td>
<td>{@link android.os.Bundle#getFloat(String) getFloat()}</td>
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 @@
<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 @@
<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 @@
<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 @@
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 @@
<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 @@
</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 @@
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 @@
// 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 @@
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 @@
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 @@
};
};
};
-</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 @@
</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 @@
</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/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 @@
<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 @@
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 bae3985..fda233d 100644
--- a/docs/html/tools/devices/emulator.jd
+++ b/docs/html/tools/devices/emulator.jd
@@ -898,7 +898,7 @@
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>
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 @@
<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 @@
<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 @@
<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 @@
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.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 @@
</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 @@
</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 @@
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 @@
</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/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 @@
</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 @@
<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/cardflip.jd b/docs/html/training/animation/cardflip.jd
index 1477f9fa..48fbbd8 100644
--- a/docs/html/training/animation/cardflip.jd
+++ b/docs/html/training/animation/cardflip.jd
@@ -70,7 +70,7 @@
<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 @@
// Commit the transaction.
.commit();
}
-</pre>
\ No newline at end of file
+</pre>
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 @@
<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/network-ops/connecting.jd b/docs/html/training/basics/network-ops/connecting.jd
index ac8d993..50a9e1b 100644
--- a/docs/html/training/basics/network-ops/connecting.jd
+++ b/docs/html/training/basics/network-ops/connecting.jd
@@ -136,7 +136,7 @@
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
- new DownloadWebpageText().execute(stringUrl);
+ new DownloadWebpageTask().execute(stringUrl);
} else {
textView.setText("No network connection available.");
}
@@ -147,7 +147,7 @@
// has been established, the AsyncTask downloads the contents of the webpage as
// an InputStream. Finally, the InputStream is converted into a string, which is
// displayed in the UI by the AsyncTask's onPostExecute method.
- private class DownloadWebpageText extends AsyncTask<String, Void, String> {
+ private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... urls) {
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 @@
// 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 @@
{@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 @@
<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/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 @@
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 @@
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 @@
<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 @@
<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 @@
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/renderscript/FileA3D.java b/graphics/java/android/renderscript/FileA3D.java
index 42b508b..8b0222a 100644
--- a/graphics/java/android/renderscript/FileA3D.java
+++ b/graphics/java/android/renderscript/FileA3D.java
@@ -28,6 +28,7 @@
import android.util.TypedValue;
/**
+ * @hide
* @deprecated in API 16
* FileA3D allows users to load Renderscript objects from files
* or resources stored on disk. It could be used to load items
diff --git a/graphics/java/android/renderscript/Font.java b/graphics/java/android/renderscript/Font.java
index 8a49abb..1a8d5bf 100644
--- a/graphics/java/android/renderscript/Font.java
+++ b/graphics/java/android/renderscript/Font.java
@@ -30,8 +30,9 @@
import android.util.TypedValue;
/**
+ * @hide
* @deprecated in API 16
- * <p>This class gives users a simple way to draw hardware accelerated text.
+ * <p>This class gives users a simple way to draw hardware accelerated text.
* Internally, the glyphs are rendered using the Freetype library and an internal cache of
* rendered glyph bitmaps is maintained. Each font object represents a combination of a typeface,
* and point size. You can create multiple font objects to represent styles such as bold or italic text,
@@ -43,7 +44,7 @@
* render large batches of text in sequence. It is also more efficient to render multiple
* characters at once instead of one by one to improve draw call batching.</p>
* <p>Font color and transparency are not part of the font object and you can freely modify
- * them in the script to suit the user's rendering needs. Font colors work as a state machine.
+ * them in the script to suit the user's rendering needs. Font colors work as a state machine.
* Every new call to draw text uses the last color set in the script.</p>
**/
public class Font extends BaseObj {
diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java
index 7210513..d0d383d 100644
--- a/graphics/java/android/renderscript/Mesh.java
+++ b/graphics/java/android/renderscript/Mesh.java
@@ -21,6 +21,7 @@
import android.util.Log;
/**
+ * @hide
* @deprecated in API 16
* <p>This class is a container for geometric data displayed with
* Renderscript. Internally, a mesh is a collection of allocations that
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index d9f64c6..9bd103e 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -26,6 +26,7 @@
/**
+ * @hide
*
* Program is a base class for all the objects that modify
* various stages of the graphics pipeline
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index 69968ac..dd0f9f5 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -21,6 +21,7 @@
/**
+ * @hide
* @deprecated in API 16
* <p>The Renderscript fragment program, also known as fragment shader is responsible
* for manipulating pixel data in a user defined way. It's constructed from a GLSL
diff --git a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
index 848c5a3..8ae1777 100644
--- a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
+++ b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
@@ -21,6 +21,7 @@
/**
+ * @hide
* @deprecated in API 16
* <p>ProgramFragmentFixedFunction is a helper class that provides
* a way to make a simple fragment shader without writing any
diff --git a/graphics/java/android/renderscript/ProgramRaster.java b/graphics/java/android/renderscript/ProgramRaster.java
index c44521b..216cb4e 100644
--- a/graphics/java/android/renderscript/ProgramRaster.java
+++ b/graphics/java/android/renderscript/ProgramRaster.java
@@ -21,6 +21,7 @@
/**
+ * @hide
* @deprecated in API 16
* Program raster is primarily used to specify whether point sprites are enabled and to control
* the culling mode. By default, back faces are culled.
diff --git a/graphics/java/android/renderscript/ProgramStore.java b/graphics/java/android/renderscript/ProgramStore.java
index d0fd6e5..dac9e76 100644
--- a/graphics/java/android/renderscript/ProgramStore.java
+++ b/graphics/java/android/renderscript/ProgramStore.java
@@ -21,6 +21,7 @@
/**
+ * @hide
* <p>ProgramStore contains a set of parameters that control how
* the graphics hardware handles writes to the framebuffer.
* It could be used to:</p>
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index 2bd5124..50e32f6 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -14,7 +14,8 @@
* limitations under the License.
*/
- /**
+/**
+ * @hide
* <p>The Renderscript vertex program, also known as a vertex shader, describes a stage in
* the graphics pipeline responsible for manipulating geometric data in a user-defined way.
* The object is constructed by providing the Renderscript system with the following data:</p>
@@ -43,6 +44,7 @@
/**
+ * @hide
* @deprecated in API 16
* ProgramVertex, also know as a vertex shader, describes a
* stage in the graphics pipeline responsible for manipulating
@@ -76,14 +78,15 @@
}
/**
- * @deprecated in API 16
- * Builder class for creating ProgramVertex objects.
- * The builder starts empty and the user must minimally provide
- * the GLSL shader code, and the varying inputs. Constant, or
- * uniform parameters to the shader may optionally be provided as
- * well.
- *
- **/
+ * @hide
+ * @deprecated in API 16
+ * Builder class for creating ProgramVertex objects.
+ * The builder starts empty and the user must minimally provide
+ * the GLSL shader code, and the varying inputs. Constant, or
+ * uniform parameters to the shader may optionally be provided as
+ * well.
+ *
+ **/
public static class Builder extends BaseProgramBuilder {
/**
* @deprecated in API 16
diff --git a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
index 88cade4..ad486f3 100644
--- a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
+++ b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
@@ -22,6 +22,7 @@
/**
+ * @hide
* @deprecated in API 16
* ProgramVertexFixedFunction is a helper class that provides a
* simple way to create a fixed function emulation vertex shader
diff --git a/graphics/java/android/renderscript/RSSurfaceView.java b/graphics/java/android/renderscript/RSSurfaceView.java
index 82ed95c..3c6c720 100644
--- a/graphics/java/android/renderscript/RSSurfaceView.java
+++ b/graphics/java/android/renderscript/RSSurfaceView.java
@@ -30,6 +30,7 @@
import android.view.SurfaceView;
/**
+ * @hide
* @deprecated in API 16
* The Surface View for a graphics renderscript (RenderScriptGL) to draw on.
*
diff --git a/graphics/java/android/renderscript/RSTextureView.java b/graphics/java/android/renderscript/RSTextureView.java
index ed04000..7eeeeae 100644
--- a/graphics/java/android/renderscript/RSTextureView.java
+++ b/graphics/java/android/renderscript/RSTextureView.java
@@ -29,6 +29,7 @@
import android.view.TextureView;
/**
+ * @hide
* @deprecated in API 16
* The Texture View for a graphics renderscript (RenderScriptGL)
* to draw on.
diff --git a/graphics/java/android/renderscript/RenderScriptGL.java b/graphics/java/android/renderscript/RenderScriptGL.java
index 5269405..52034b1 100644
--- a/graphics/java/android/renderscript/RenderScriptGL.java
+++ b/graphics/java/android/renderscript/RenderScriptGL.java
@@ -29,6 +29,7 @@
import android.view.SurfaceView;
/**
+ * @hide
* @deprecated in API 16
* The Graphics derivitive of Renderscript. Extends the basic context to add a
* root script which is the display window for graphical output. When the
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 @@
* @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 @@
* @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 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/graphics/java/android/renderscript/package.html b/graphics/java/android/renderscript/package.html
index 5eab23c..eb178c1 100644
--- a/graphics/java/android/renderscript/package.html
+++ b/graphics/java/android/renderscript/package.html
@@ -1,86 +1,10 @@
<HTML>
<BODY>
-<p>The Renderscript rendering and computational APIs offer a low-level, high performance means of
-carrying out mathematical calculations and 3D graphics rendering.</p>
+<p>RenderScript provides support for high-performance computation across heterogeneous processors.</p>
<p>For more information, see the
-<a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a> developer guide.</p>
+<a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
{@more}
-<p>An example of Renderscript in applications include the 3D carousel view that is present in
-Android 3.0 applications such as the Books and YouTube applications. This API is intended for
-developers who are comfortable working with native code and want to maximize their performance
-critical applications.</p>
-
-<p>Renderscript adopts a control and slave architecture where the low-level native code is controlled by the
-higher level Android system that runs in the virtual machine (VM). The VM code handles resource
-allocation and lifecycle management of the Renderscript enabled application and calls the Renderscript
-code through high level entry points. The Android build tools generate these entry points through reflection on
-the native Renderscript code, which you write in C (C99 standard). The Renderscript code
-does the intensive computation and returns the result back to the Android VM.</p>
-
-<p>You can find the Renderscript native
-APIs in the <code><sdk_root>/platforms/android-11/renderscript</code> directory.
-The Android system APIs are broken into a few main groups:</p>
-
-<h4>Core</h4>
-<p>These classes are used internally by the system for memory allocation. They are used by the classes that
-are generated by the build tools:</p>
-<ul>
- <li>Allocation</li>
- <li>Element</li>
- <li>Type</li>
- <li>Script</li>
-</ul>
-
-
-<h4>Data Types</h4>
-<p>These data types are used by the classes that are generated
-by the build tools. They are the reflected counterparts of the native data types that
-are defined by the native Renderscript APIs and used by your Renderscript code. The
-classes include:</p>
-<ul>
- <li>Byte2, Byte3, and Byte4</li>
- <li>Float2, Float3, Float4</li>
- <li>Int2, Int3, Int4</li>
- <li>Long2, Long3, Long4</li>
- <li>Matrix2f, Matrix3f, Matrix4f</li>
- <li>Short2, Short3, Short4</li>
-</ul>
-
-<p>For example, if you declared the following struct in your .rs Renderscript file:</p>
-
-<pre>struct Hello { float3 position; rs_matrix4x4 transform; }</pre>
-
-<p>The build tools generate a class through reflection that looks like the following:</p>
-<pre>
-class Hello {
- static public class Item {
- Float4 position;
- Matrix4f transform;
- }
-Element createElement(RenderScript rs) {
- Element.Builder eb = new Element.Builder(rs);
- eb.add(Element.F32_3(rs), "position");
- eb.add(Element.MATRIX_4X4(rs), "transform");
- return eb.create();
- }
-}
-</pre>
-
-<h4>Graphics</h4>
-<p>These classes are specific to graphics Renderscripts and support a typical rendering
-pipeline.</p>
-<ul>
-<li>Mesh</li>
-<li>ProgramFragment</li>
-<li>ProgramRaster</li>
-<li>ProgramStore</li>
-<li>ProgramVertex</li>
-<li>RSSurfaceView</li>
-<li>Sampler</li>
-</ul>
-
-</p>
</BODY>
</HTML>
diff --git a/include/androidfw/InputDevice.h b/include/androidfw/InputDevice.h
index 1aecf80..45dc2db 100644
--- a/include/androidfw/InputDevice.h
+++ b/include/androidfw/InputDevice.h
@@ -64,6 +64,7 @@
float max;
float flat;
float fuzz;
+ float resolution;
};
void initialize(int32_t id, int32_t generation, const InputDeviceIdentifier& identifier,
@@ -83,7 +84,7 @@
void addSource(uint32_t source);
void addMotionRange(int32_t axis, uint32_t source,
- float min, float max, float flat, float fuzz);
+ float min, float max, float flat, float fuzz, float resolution);
void addMotionRange(const MotionRange& range);
inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
diff --git a/include/androidfw/InputTransport.h b/include/androidfw/InputTransport.h
index 5706bce..8712995 100644
--- a/include/androidfw/InputTransport.h
+++ b/include/androidfw/InputTransport.h
@@ -168,6 +168,9 @@
*/
status_t receiveMessage(InputMessage* msg);
+ /* Returns a new object that has a duplicate of this channel's fd. */
+ sp<InputChannel> dup() const;
+
private:
String8 mName;
int mFd;
diff --git a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
index 83faf35..18225a5 100644
--- a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
@@ -49,8 +49,6 @@
* <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 {
private final String mKeystoreAlias;
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 @@
}
}
+ /**
+ * 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 2037472..852f0bb 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -305,6 +305,15 @@
}
}
+ 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/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp
index fe891cb..f742052 100644
--- a/libs/androidfw/InputDevice.cpp
+++ b/libs/androidfw/InputDevice.cpp
@@ -172,8 +172,8 @@
}
void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max,
- float flat, float fuzz) {
- MotionRange range = { axis, source, min, max, flat, fuzz };
+ float flat, float fuzz, float resolution) {
+ MotionRange range = { axis, source, min, max, flat, fuzz, resolution };
mMotionRanges.add(range);
}
diff --git a/libs/androidfw/InputTransport.cpp b/libs/androidfw/InputTransport.cpp
index 351c666..498389ea 100644
--- a/libs/androidfw/InputTransport.cpp
+++ b/libs/androidfw/InputTransport.cpp
@@ -219,6 +219,11 @@
return OK;
}
+sp<InputChannel> InputChannel::dup() const {
+ int fd = ::dup(getFd());
+ return fd >= 0 ? new InputChannel(getName(), fd) : NULL;
+}
+
// --- InputPublisher ---
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 5ff92be..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
/////////////////////////////////////////////////////////////////////////////////
@@ -270,7 +273,7 @@
while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop();
- storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + 1);
+ storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + FLUSH_SAVE_STACK_DEPTH);
}
void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
@@ -386,6 +389,8 @@
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 3e450da..653f315 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -52,8 +52,6 @@
kOpBatch_Count, // Add other batch ids before this
};
- void clear();
-
bool isEmpty() { return mBatches.isEmpty(); }
/**
@@ -80,6 +78,8 @@
*/
void resetBatchingState();
+ void clear();
+
void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op);
void storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op, int newSaveCount);
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 @@
}
}
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 @@
}
}
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 @@
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 9c3d058..a5dee9f 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -165,7 +165,11 @@
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) {
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index e80b325..9bc5c14 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -21,38 +21,46 @@
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Must be a power of two
-#define DITHER_KERNEL_SIZE 4
-
-///////////////////////////////////////////////////////////////////////////////
// Lifecycle
///////////////////////////////////////////////////////////////////////////////
void Dither::bindDitherTexture() {
if (!mInitialized) {
- const uint8_t pattern[] = {
- 0, 8, 2, 10,
- 12, 4, 14, 6,
- 3, 11, 1, 9,
- 15, 7, 13, 5
- };
+ bool useFloatTexture = Extensions::getInstance().getMajorGlVersion() >= 3;
glGenTextures(1, &mDitherTexture);
glBindTexture(GL_TEXTURE_2D, mDitherTexture);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
+ if (useFloatTexture) {
+ float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE);
+ const GLfloat pattern[] = {
+ 0 * dither, 8 * dither, 2 * dither, 10 * dither,
+ 12 * dither, 4 * dither, 14 * dither, 6 * dither,
+ 3 * dither, 11 * dither, 1 * dither, 9 * dither,
+ 15 * dither, 7 * dither, 13 * dither, 5 * dither
+ };
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, sizeof(GLfloat));
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
+ GL_RED, GL_FLOAT, &pattern);
+ } else {
+ const uint8_t pattern[] = {
+ 0, 8, 2, 10,
+ 12, 4, 14, 6,
+ 3, 11, 1, 9,
+ 15, 7, 13, 5
+ };
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
+ }
mInitialized = true;
} else {
@@ -76,10 +84,7 @@
bindDitherTexture();
- float ditherSize = 1.0f / DITHER_KERNEL_SIZE;
glUniform1i(program->getUniform("ditherSampler"), textureSlot);
- glUniform1f(program->getUniform("ditherSize"), ditherSize);
- glUniform1f(program->getUniform("ditherSizeSquared"), ditherSize * ditherSize);
}
}; // namespace uirenderer
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 34cf9bf..4d1f921 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -17,13 +17,23 @@
#ifndef ANDROID_HWUI_DITHER_H
#define ANDROID_HWUI_DITHER_H
-#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
#include "Program.h"
namespace android {
namespace uirenderer {
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Must be a power of two
+#define DITHER_KERNEL_SIZE 4
+// These must not use the .0f notation as they are used from GLSL
+#define DITHER_KERNEL_SIZE_INV (1.0 / 4.0)
+#define DITHER_KERNEL_SIZE_INV_SQUARE (1.0 / 16.0)
+
/**
* Handles dithering for programs.
*/
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index edc90fb..51aec8d 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -64,10 +64,32 @@
mHas4BitStencil = hasExtension("GL_OES_stencil4");
mExtensions = strdup(buffer);
+
+ const char* version = (const char*) glGetString(GL_VERSION);
+ mVersion = strdup(version);
+
+ // Section 6.1.5 of the OpenGL ES specification indicates the GL version
+ // string strictly follows this format:
+ //
+ // OpenGL<space>ES<space><version number><space><vendor-specific information>
+ //
+ // In addition section 6.1.5 describes the version number thusly:
+ //
+ // "The version number is either of the form major number.minor number or
+ // major number.minor number.release number, where the numbers all have one
+ // or more digits. The release number and vendor specific information are
+ // optional."
+
+ if (sscanf(version, "OpenGL ES %d.%d", &mVersionMajor, &mVersionMinor) !=2) {
+ // If we cannot parse the version number, assume OpenGL ES 2.0
+ mVersionMajor = 2;
+ mVersionMinor = 0;
+ }
}
Extensions::~Extensions() {
free(mExtensions);
+ free(mVersion);
}
///////////////////////////////////////////////////////////////////////////////
@@ -80,6 +102,7 @@
}
void Extensions::dump() const {
+ ALOGD("%s", mVersion);
ALOGD("Supported extensions:\n%s", mExtensions);
}
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index a069a6a..54a3987 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -45,6 +45,9 @@
inline bool has1BitStencil() const { return mHas1BitStencil; }
inline bool has4BitStencil() const { return mHas4BitStencil; }
+ inline int getMajorGlVersion() const { return mVersionMajor; }
+ inline int getMinorGlVersion() const { return mVersionMinor; }
+
bool hasExtension(const char* extension) const;
void dump() const;
@@ -55,6 +58,7 @@
SortedVector<String8> mExtensionList;
char* mExtensions;
+ char* mVersion;
bool mHasNPot;
bool mHasFramebufferFetch;
@@ -64,6 +68,9 @@
bool mHasTiledRendering;
bool mHas1BitStencil;
bool mHas4BitStencil;
+
+ int mVersionMajor;
+ int mVersionMinor;
}; // class Extensions
}; // namespace uirenderer
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 26c7e5d..44dc731 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -31,6 +31,7 @@
#include "Caches.h"
#include "Debug.h"
+#include "Extensions.h"
#include "FontRenderer.h"
#include "Rect.h"
@@ -375,34 +376,60 @@
Caches& caches = Caches::getInstance();
GLuint lastTextureId = 0;
+
+ // OpenGL ES 3.0+ lets us specify the row length for unpack operations such
+ // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
+ // With OpenGL ES 2.0 we have to upload entire stripes instead.
+ const bool hasUnpackRowLength = Extensions::getInstance().getMajorGlVersion() >= 3;
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
// Iterate over all the cache textures and see which ones need to be updated
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
- // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
- // of data. So expand the dirty rect to the encompassing horizontal stripe.
const Rect* dirtyRect = cacheTexture->getDirtyRect();
- uint32_t x = 0;
+ uint32_t x = hasUnpackRowLength ? dirtyRect->left : 0;
uint32_t y = dirtyRect->top;
uint32_t width = cacheTexture->getWidth();
uint32_t height = dirtyRect->getHeight();
- void* textureData = cacheTexture->getTexture() + y * width;
+ void* textureData = cacheTexture->getTexture() + y * width + x;
if (cacheTexture->getTextureId() != lastTextureId) {
lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
glBindTexture(GL_TEXTURE_2D, lastTextureId);
+
+ // The unpack row length only needs to be specified when a new
+ // texture is bound
+ if (hasUnpackRowLength) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
+ }
}
+
+ // If we can upload a sub-rectangle, use the dirty rect width
+ // instead of the width of the entire texture
+ if (hasUnpackRowLength) {
+ width = dirtyRect->getWidth();
+ }
+
#if DEBUG_FONT_RENDERER
ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
i, x, y, width, height);
#endif
+
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
+
cacheTexture->setDirty(false);
}
}
+ // Reset to default unpack row length to avoid affecting texture
+ // uploads in other parts of the renderer
+ if (hasUnpackRowLength) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
+
mUploadTexture = false;
}
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index d681609..eef366c 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -27,13 +27,6 @@
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define GRADIENT_TEXTURE_HEIGHT 2
-#define GRADIENT_BYTES_PER_PIXEL 4
-
-///////////////////////////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////////////////////////
@@ -83,6 +76,10 @@
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
mCache.setOnEntryRemovedListener(this);
+
+ const Extensions& extensions = Extensions::getInstance();
+ mUseFloatTexture = extensions.getMajorGlVersion() >= 3;
+ mHasNpot = extensions.hasNPot();
}
GradientCache::GradientCache(uint32_t maxByteSize):
@@ -120,7 +117,7 @@
void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
if (texture) {
- const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
+ const uint32_t size = texture->width * texture->height * bytesPerPixel();
mSize -= size;
glDeleteTextures(1, &texture->id);
@@ -151,7 +148,7 @@
GradientInfo& info) {
uint32_t width = 256 * (count - 1);
- if (!Extensions::getInstance().hasNPot()) {
+ if (!mHasNpot) {
width = 1 << (31 - __builtin_clz(width));
}
@@ -175,12 +172,12 @@
Texture* texture = new Texture;
texture->width = info.width;
- texture->height = GRADIENT_TEXTURE_HEIGHT;
+ texture->height = 2;
texture->blend = info.hasAlpha;
texture->generation = 1;
// Asume the cache is always big enough
- const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
+ const uint32_t size = texture->width * texture->height * bytesPerPixel();
while (getSize() + size > mMaxSize) {
mCache.removeOldest();
}
@@ -193,69 +190,110 @@
return texture;
}
+size_t GradientCache::bytesPerPixel() const {
+ // We use 4 channels (RGBA)
+ return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
+}
+
+void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const {
+ outColor.r = (inColor >> 16) & 0xff;
+ outColor.g = (inColor >> 8) & 0xff;
+ outColor.b = (inColor >> 0) & 0xff;
+ outColor.a = (inColor >> 24) & 0xff;
+}
+
+void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const {
+ outColor.r = ((inColor >> 16) & 0xff) / 255.0f;
+ outColor.g = ((inColor >> 8) & 0xff) / 255.0f;
+ outColor.b = ((inColor >> 0) & 0xff) / 255.0f;
+ outColor.a = ((inColor >> 24) & 0xff) / 255.0f;
+}
+
+void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount,
+ uint8_t*& dst) const {
+ float oppAmount = 1.0f - amount;
+ const float alpha = start.a * oppAmount + end.a * amount;
+ const float a = alpha / 255.0f;
+
+ *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount));
+ *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount));
+ *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount));
+ *dst++ = uint8_t(alpha);
+}
+
+void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount,
+ uint8_t*& dst) const {
+ float oppAmount = 1.0f - amount;
+ const float a = start.a * oppAmount + end.a * amount;
+
+ float* d = (float*) dst;
+ *d++ = a * (start.r * oppAmount + end.r * amount);
+ *d++ = a * (start.g * oppAmount + end.g * amount);
+ *d++ = a * (start.b * oppAmount + end.b * amount);
+ *d++ = a;
+
+ dst += 4 * sizeof(float);
+}
+
void GradientCache::generateTexture(uint32_t* colors, float* positions,
int count, Texture* texture) {
-
const uint32_t width = texture->width;
- const GLsizei rowBytes = width * GRADIENT_BYTES_PER_PIXEL;
- uint32_t pixels[width * texture->height];
+ const GLsizei rowBytes = width * bytesPerPixel();
+ uint8_t pixels[rowBytes * texture->height];
+
+ static ChannelSplitter gSplitters[] = {
+ &android::uirenderer::GradientCache::splitToBytes,
+ &android::uirenderer::GradientCache::splitToFloats,
+ };
+ ChannelSplitter split = gSplitters[mUseFloatTexture];
+
+ static ChannelMixer gMixers[] = {
+ &android::uirenderer::GradientCache::mixBytes,
+ &android::uirenderer::GradientCache::mixFloats,
+ };
+ ChannelMixer mix = gMixers[mUseFloatTexture];
+
+ GradientColor start;
+ (this->*split)(colors[0], start);
+
+ GradientColor end;
+ (this->*split)(colors[1], end);
int currentPos = 1;
+ float startPos = positions[0];
+ float distance = positions[1] - startPos;
- float startA = (colors[0] >> 24) & 0xff;
- float startR = (colors[0] >> 16) & 0xff;
- float startG = (colors[0] >> 8) & 0xff;
- float startB = (colors[0] >> 0) & 0xff;
-
- float endA = (colors[1] >> 24) & 0xff;
- float endR = (colors[1] >> 16) & 0xff;
- float endG = (colors[1] >> 8) & 0xff;
- float endB = (colors[1] >> 0) & 0xff;
-
- float start = positions[0];
- float distance = positions[1] - start;
-
- uint8_t* p = (uint8_t*) pixels;
+ uint8_t* dst = pixels;
for (uint32_t x = 0; x < width; x++) {
float pos = x / float(width - 1);
if (pos > positions[currentPos]) {
- startA = endA;
- startR = endR;
- startG = endG;
- startB = endB;
- start = positions[currentPos];
+ start = end;
+ startPos = positions[currentPos];
currentPos++;
- endA = (colors[currentPos] >> 24) & 0xff;
- endR = (colors[currentPos] >> 16) & 0xff;
- endG = (colors[currentPos] >> 8) & 0xff;
- endB = (colors[currentPos] >> 0) & 0xff;
- distance = positions[currentPos] - start;
+ (this->*split)(colors[currentPos], end);
+ distance = positions[currentPos] - startPos;
}
- float amount = (pos - start) / distance;
- float oppAmount = 1.0f - amount;
-
- const float alpha = startA * oppAmount + endA * amount;
- const float a = alpha / 255.0f;
- *p++ = uint8_t(a * (startR * oppAmount + endR * amount));
- *p++ = uint8_t(a * (startG * oppAmount + endG * amount));
- *p++ = uint8_t(a * (startB * oppAmount + endB * amount));
- *p++ = uint8_t(alpha);
+ float amount = (pos - startPos) / distance;
+ (this->*mix)(start, end, amount, dst);
}
- for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
- memcpy(pixels + width * i, pixels, rowBytes);
- }
+ memcpy(pixels + rowBytes, pixels, rowBytes);
glGenTextures(1, &texture->id);
-
glBindTexture(GL_TEXTURE_2D, texture->id);
- glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ if (mUseFloatTexture) {
+ // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0,
+ GL_RGBA, GL_FLOAT, pixels);
+ } else {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ }
texture->setFilter(GL_LINEAR);
texture->setWrap(GL_CLAMP_TO_EDGE);
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 7dc5b8a..43934d9 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_HWUI_GRADIENT_CACHE_H
#define ANDROID_HWUI_GRADIENT_CACHE_H
-#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
#include <SkShader.h>
@@ -160,12 +160,35 @@
void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);
+ size_t bytesPerPixel() const;
+
+ struct GradientColor {
+ float r;
+ float g;
+ float b;
+ float a;
+ };
+
+ typedef void (GradientCache::*ChannelSplitter)(uint32_t inColor,
+ GradientColor& outColor) const;
+
+ void splitToBytes(uint32_t inColor, GradientColor& outColor) const;
+ void splitToFloats(uint32_t inColor, GradientColor& outColor) const;
+
+ typedef void (GradientCache::*ChannelMixer)(GradientColor& start, GradientColor& end,
+ float amount, uint8_t*& dst) const;
+
+ void mixBytes(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;
+ void mixFloats(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;
+
LruCache<GradientCacheEntry, Texture*> mCache;
uint32_t mSize;
uint32_t mMaxSize;
GLint mMaxTextureSize;
+ bool mUseFloatTexture;
+ bool mHasNpot;
Vector<SkShader*> mGarbage;
mutable Mutex mLock;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 63bb73f..a718294 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -90,7 +90,7 @@
if (fbo) {
Caches::getInstance().activeTexture(0);
bindTexture();
- allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
+ allocateTexture();
if (glGetError() != GL_NO_ERROR) {
setSize(oldWidth, oldHeight);
@@ -167,7 +167,6 @@
displayList->defer(deferredState, 0);
deferredUpdateScheduled = false;
- displayList = NULL;
}
void Layer::flush() {
@@ -182,7 +181,7 @@
renderer = NULL;
dirtyRect.setEmpty();
- deferredList->clear();
+ displayList = NULL;
}
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 27e0cf1..715dfa4 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -255,13 +255,14 @@
texture.id = 0;
}
- inline void allocateTexture(GLenum format, GLenum storage) {
+ inline void allocateTexture() {
#if DEBUG_LAYERS
ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
#endif
if (texture.id) {
- glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0,
- format, storage, NULL);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 8451048..3e55fff 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -256,7 +256,7 @@
// Initialize the texture if needed
if (layer->isEmpty()) {
layer->setEmpty(false);
- layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
+ layer->allocateTexture();
// This should only happen if we run out of memory
if (glGetError() != GL_NO_ERROR) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 4a5785c..3730017 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -114,6 +114,7 @@
mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
mDrawModifiers.mShader = NULL;
mDrawModifiers.mColorFilter = NULL;
+ mDrawModifiers.mOverrideLayerAlpha = 1.0f;
mDrawModifiers.mHasShadow = false;
mDrawModifiers.mHasDrawFilter = false;
@@ -570,8 +571,8 @@
startMark("Defer Layer Updates");
}
- // Note: it is very important to update the layers in reverse order
- for (int i = count - 1; i >= 0; i--) {
+ // Note: it is very important to update the layers in order
+ for (int i = 0; i < count; i++) {
Layer* layer = mLayerUpdates.itemAt(i);
updateLayer(layer, false);
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
@@ -593,8 +594,8 @@
startMark("Apply Layer Updates");
char layerName[12];
- // Note: it is very important to update the layers in reverse order
- for (int i = count - 1; i >= 0; i--) {
+ // Note: it is very important to update the layers in order
+ for (int i = 0; i < count; i++) {
sprintf(layerName, "Layer #%d", i);
startMark(layerName);
@@ -921,7 +922,7 @@
// Initialize the texture if needed
if (layer->isEmpty()) {
- layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
+ layer->allocateTexture();
layer->setEmpty(false);
}
@@ -1074,7 +1075,7 @@
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,
@@ -1112,7 +1113,7 @@
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();
@@ -2237,7 +2238,7 @@
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);
@@ -2990,7 +2991,7 @@
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);
@@ -3446,10 +3447,24 @@
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 04a47fc..dd7a5a2 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -51,6 +51,7 @@
struct DrawModifiers {
SkiaShader* mShader;
SkiaColorFilter* mColorFilter;
+ float mOverrideLayerAlpha;
// Drop shadow
bool mHasShadow;
@@ -275,6 +276,9 @@
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);
@@ -283,7 +287,6 @@
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 @@
/**
* 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 @@
/**
* 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 @@
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/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index f78fb2d..2479630 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -19,6 +19,7 @@
#include <utils/String8.h>
#include "Caches.h"
+#include "Dither.h"
#include "ProgramCache.h"
namespace android {
@@ -32,6 +33,9 @@
#define MODULATE_OP_MODULATE 1
#define MODULATE_OP_MODULATE_A8 2
+#define STR(x) STR1(x)
+#define STR1(x) #x
+
///////////////////////////////////////////////////////////////////////////////
// Vertex shaders snippets
///////////////////////////////////////////////////////////////////////////////
@@ -51,17 +55,8 @@
"uniform mat4 transform;\n";
const char* gVS_Header_Uniforms_IsPoint =
"uniform mediump float pointSize;\n";
-const char* gVS_Header_Uniforms_HasGradient[3] = {
- // Linear
- "uniform mat4 screenSpace;\n"
- "uniform float ditherSize;\n",
- // Circular
- "uniform mat4 screenSpace;\n"
- "uniform float ditherSize;\n",
- // Sweep
- "uniform mat4 screenSpace;\n"
- "uniform float ditherSize;\n"
-};
+const char* gVS_Header_Uniforms_HasGradient =
+ "uniform mat4 screenSpace;\n";
const char* gVS_Header_Uniforms_HasBitmap =
"uniform mat4 textureTransform;\n"
"uniform mediump vec2 textureDimension;\n";
@@ -105,21 +100,21 @@
const char* gVS_Main_OutGradient[6] = {
// Linear
" linear = vec2((screenSpace * position).x, 0.5);\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
" linear = (screenSpace * position).x;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
// Circular
" circular = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
" circular = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
// Sweep
" sweep = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
" sweep = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
};
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
@@ -153,24 +148,14 @@
"uniform sampler2D baseSampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
"uniform samplerExternalOES baseSampler;\n";
-#define FS_UNIFORMS_DITHER \
- "uniform float ditherSizeSquared;\n" \
- "uniform sampler2D ditherSampler;\n"
-#define FS_UNIFORMS_GRADIENT \
- "uniform vec4 startColor;\n" \
+const char* gFS_Uniforms_Dither =
+ "uniform sampler2D ditherSampler;";
+const char* gFS_Uniforms_GradientSampler[2] = {
+ "%s\n"
+ "uniform sampler2D gradientSampler;\n",
+ "%s\n"
+ "uniform vec4 startColor;\n"
"uniform vec4 endColor;\n"
-const char* gFS_Uniforms_GradientSampler[6] = {
- // Linear
- FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
- FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,
-
- // Circular
- FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
- FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,
-
- // Sweep
- FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
- FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT
};
const char* gFS_Uniforms_BitmapSampler =
"uniform sampler2D bitmapSampler;\n";
@@ -197,10 +182,14 @@
" highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
"((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
-#define FS_MAIN_DITHER \
- "texture2D(ditherSampler, ditherTexCoords).a * ditherSizeSquared"
+const char* gFS_Main_Dither[2] = {
+ // ES 2.0
+ "texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
+ // ES 3.0
+ "texture2D(ditherSampler, ditherTexCoords).r"
+};
const char* gFS_Main_AddDitherToGradient =
- " gradientColor += " FS_MAIN_DITHER ";\n";
+ " gradientColor += %s;\n";
// Fast cases
const char* gFS_Fast_SingleColor =
@@ -233,18 +222,18 @@
"}\n\n";
const char* gFS_Fast_SingleGradient[2] = {
"\nvoid main(void) {\n"
- " gl_FragColor = " FS_MAIN_DITHER " + texture2D(gradientSampler, linear);\n"
+ " gl_FragColor = %s + texture2D(gradientSampler, linear);\n"
"}\n\n",
"\nvoid main(void) {\n"
- " gl_FragColor = " FS_MAIN_DITHER " + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
- "}\n\n"
+ " gl_FragColor = %s + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+ "}\n\n",
};
const char* gFS_Fast_SingleModulateGradient[2] = {
"\nvoid main(void) {\n"
- " gl_FragColor = " FS_MAIN_DITHER " + color.a * texture2D(gradientSampler, linear);\n"
+ " gl_FragColor = %s + color.a * texture2D(gradientSampler, linear);\n"
"}\n\n",
"\nvoid main(void) {\n"
- " gl_FragColor = " FS_MAIN_DITHER " + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+ " gl_FragColor = %s + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
"}\n\n"
};
@@ -410,7 +399,7 @@
// Constructors/destructors
///////////////////////////////////////////////////////////////////////////////
-ProgramCache::ProgramCache() {
+ProgramCache::ProgramCache(): mHasES3(Extensions::getInstance().getMajorGlVersion() >= 3) {
}
ProgramCache::~ProgramCache() {
@@ -484,7 +473,7 @@
shader.append(gVS_Header_Uniforms_TextureTransform);
}
if (description.hasGradient) {
- shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]);
+ shader.append(gVS_Header_Uniforms_HasGradient);
}
if (description.hasBitmap) {
shader.append(gVS_Header_Uniforms_HasBitmap);
@@ -601,7 +590,8 @@
shader.append(gFS_Uniforms_ExternalTextureSampler);
}
if (description.hasGradient) {
- shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]);
+ shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
+ gFS_Uniforms_Dither);
}
if (description.hasBitmap && description.isPoint) {
shader.append(gFS_Header_Uniforms_PointHasBitmap);
@@ -652,9 +642,11 @@
fast = true;
} else if (singleGradient) {
if (!description.modulate) {
- shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
+ shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient],
+ gFS_Main_Dither[mHasES3]);
} else {
- shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
+ shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient],
+ gFS_Main_Dither[mHasES3]);
}
fast = true;
}
@@ -708,7 +700,7 @@
}
if (description.hasGradient) {
shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
- shader.append(gFS_Main_AddDitherToGradient);
+ shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
}
if (description.hasBitmap) {
if (description.isPoint) {
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 1ca148d..38f6f99 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -57,6 +57,8 @@
void printLongString(const String8& shader) const;
KeyedVector<programid, Program*> mCache;
+
+ const bool mHasES3;
}; // class ProgramCache
}; // namespace uirenderer
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 1096642..577f463 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -15,10 +15,9 @@
*/
#include <SkGlyph.h>
-#include <utils/Log.h>
-#include "Debug.h"
#include "CacheTexture.h"
+#include "../Debug.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 5742941..e7fb474 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_HWUI_CACHE_TEXTURE_H
#define ANDROID_HWUI_CACHE_TEXTURE_H
-#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
#include <SkScalerContext.h>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 6f284f8..917a47d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -50,6 +50,7 @@
private long mVolumeKeyUpTime;
private final boolean mUseMasterVolume;
private final boolean mUseVolumeKeySounds;
+ private final Binder mToken = new Binder();
private static String TAG = "AudioManager";
/**
@@ -2075,7 +2076,8 @@
IAudioService service = getService();
try {
// pi != null
- service.registerMediaButtonIntent(pi, eventReceiver);
+ service.registerMediaButtonIntent(pi, eventReceiver,
+ eventReceiver == null ? mToken : null);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
}
@@ -2127,7 +2129,7 @@
mediaButtonIntent.setComponent(eventReceiver);
PendingIntent pi = PendingIntent.getBroadcast(mContext,
0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
- unregisterMediaButtonIntent(pi, eventReceiver);
+ unregisterMediaButtonIntent(pi);
}
/**
@@ -2139,16 +2141,16 @@
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 +2276,26 @@
}
/**
+ * @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 9ded922..773d7b6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -167,12 +167,7 @@
private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
private static final int MSG_UNLOAD_SOUND_EFFECTS = 31;
private static final int MSG_RCC_NEW_PLAYBACK_STATE = 32;
-
-
- // 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_SEEK_REQUEST = 33;
private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
// Timeout for connection to bluetooth headset service
@@ -362,7 +357,7 @@
private static final int SCO_STATE_INACTIVE = 0;
// SCO audio activation request waiting for headset service to connect
private static final int SCO_STATE_ACTIVATE_REQ = 1;
- // SCO audio state is active or starting due to a local request to start a virtual call
+ // SCO audio state is active or starting due to a request from AudioManager API
private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
// SCO audio deactivation request waiting for headset service to connect
private static final int SCO_STATE_DEACTIVATE_REQ = 5;
@@ -535,6 +530,9 @@
// Register for package removal intent broadcasts for media button receiver persistence
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
pkgFilter.addDataScheme("package");
context.registerReceiver(mReceiver, pkgFilter);
@@ -582,14 +580,10 @@
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();
}
}
@@ -633,10 +627,7 @@
}
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,
@@ -836,14 +827,9 @@
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) {
@@ -872,64 +858,40 @@
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 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 stream is muted, adjust last audible index only
- oldIndex = mStreamStates[streamType].getIndex(device,
- (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
+ int oldIndex = mStreamStates[streamType].getIndex(device);
- 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 (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);
}
@@ -969,6 +931,7 @@
};
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())) {
@@ -976,18 +939,11 @@
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) */
@@ -1006,9 +962,7 @@
// 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]);
@@ -1034,9 +988,7 @@
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);
@@ -1198,41 +1150,23 @@
* @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);
}
}
@@ -1244,7 +1178,6 @@
for (int stream = 0; stream < mStreamStates.length; stream++) {
if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
- // Bring back last audible volume
mStreamStates[stream].mute(cb, state);
}
}
@@ -1262,7 +1195,7 @@
/** get stream mute state. */
public boolean isStreamMute(int streamType) {
- return (mStreamStates[streamType].muteCount() != 0);
+ return mStreamStates[streamType].isMuted();
}
/** @see AudioManager#setMasterMute(boolean, int) */
@@ -1289,8 +1222,12 @@
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();
@@ -1347,7 +1284,7 @@
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. */
@@ -1412,7 +1349,7 @@
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();
@@ -1661,8 +1598,8 @@
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*/);
}
@@ -1896,7 +1833,7 @@
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++) {
@@ -2119,8 +2056,7 @@
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_INACTIVE) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
- if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice)) {
+ if (mBluetoothHeadset.connectAudio()) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
} else {
broadcastScoConnectionState(
@@ -2142,8 +2078,7 @@
mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
- if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice)) {
+ if (!mBluetoothHeadset.disconnectAudio()) {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -2316,12 +2251,10 @@
switch (mScoAudioState) {
case SCO_STATE_ACTIVATE_REQ:
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
+ status = mBluetoothHeadset.connectAudio();
break;
case SCO_STATE_DEACTIVATE_REQ:
- status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
+ status = mBluetoothHeadset.disconnectAudio();
break;
case SCO_STATE_DEACTIVATE_EXT_REQ:
status = mBluetoothHeadset.stopVoiceRecognition(
@@ -2396,8 +2329,7 @@
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
@@ -2742,18 +2674,14 @@
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];
@@ -2766,10 +2694,8 @@
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;
@@ -2781,14 +2707,11 @@
// 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];
@@ -2797,10 +2720,7 @@
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;
}
@@ -2814,7 +2734,7 @@
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) ?
@@ -2828,72 +2748,33 @@
// 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();
@@ -2901,22 +2782,23 @@
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) {
@@ -2926,9 +2808,6 @@
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
@@ -2939,12 +2818,10 @@
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));
}
}
}
@@ -2954,63 +2831,21 @@
}
}
- 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();
@@ -3018,11 +2853,7 @@
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);
}
}
@@ -3033,12 +2864,6 @@
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) {
@@ -3074,6 +2899,7 @@
// 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
@@ -3082,22 +2908,10 @@
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!
@@ -3115,37 +2929,25 @@
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() {
@@ -3167,6 +2969,10 @@
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;
@@ -3199,14 +3005,6 @@
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)+", ");
- }
}
}
@@ -3254,8 +3052,8 @@
sendMsg(mAudioHandler,
MSG_PERSIST_VOLUME,
SENDMSG_QUEUE,
- PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
device,
+ 0,
streamState,
PERSIST_DELAY);
@@ -3276,24 +3074,14 @@
}
}
- 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) {
@@ -3545,7 +3333,7 @@
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:
@@ -4248,14 +4036,21 @@
0,
null,
SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
- } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
+ } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
+ || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// a package is being removed, not replaced
String packageName = intent.getData().getSchemeSpecificPart();
if (packageName != null) {
- removeMediaButtonReceiverForPackage(packageName);
+ cleanupMediaButtonReceiverForPackage(packageName, true);
}
}
+ } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
+ || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
+ String packageName = intent.getData().getSchemeSpecificPart();
+ if (packageName != null) {
+ cleanupMediaButtonReceiverForPackage(packageName, false);
+ }
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
AudioSystem.setParameters("screen_state=on");
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
@@ -4919,6 +4714,9 @@
}
};
+ /**
+ * 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.
@@ -5059,8 +4857,9 @@
}
}
- private static class RemoteControlStackEntry {
+ private static class RemoteControlStackEntry implements DeathRecipient {
public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+ final public AudioService mService;
/**
* The target for the ACTION_MEDIA_BUTTON events.
* Always non null.
@@ -5071,6 +4870,7 @@
* Always non null.
*/
final public ComponentName mReceiverComponent;
+ public IBinder mToken;
public String mCallingPackageName;
public int mCallingUid;
/**
@@ -5100,10 +4900,13 @@
mRemoteVolumeObs = null;
}
- /** precondition: mediaIntent != null, eventReceiver != null */
- public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
+ /** precondition: mediaIntent != null */
+ public RemoteControlStackEntry(AudioService service, PendingIntent mediaIntent,
+ ComponentName eventReceiver, IBinder token) {
+ mService = service;
mMediaIntent = mediaIntent;
mReceiverComponent = eventReceiver;
+ mToken = token;
mCallingUid = -1;
mRcClient = null;
mRccId = ++sLastRccId;
@@ -5113,6 +4916,17 @@
RemoteControlClient.PLAYBACK_SPEED_1X);
resetPlaybackInfo();
+ if (mToken != null) {
+ try {
+ mToken.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ mService.mAudioHandler.post(new Runnable() {
+ @Override public void run() {
+ mService.unregisterMediaButtonIntent(mMediaIntent);
+ }
+ });
+ }
+ }
}
public void unlinkToRcClientDeath() {
@@ -5128,9 +4942,22 @@
}
}
+ public void destroy() {
+ unlinkToRcClientDeath();
+ if (mToken != null) {
+ mToken.unlinkToDeath(this, 0);
+ mToken = null;
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mService.unregisterMediaButtonIntent(mMediaIntent);
+ }
+
@Override
protected void finalize() throws Throwable {
- unlinkToRcClientDeath();// unlink exception handled inside method
+ destroy(); // unlink exception handled inside method
super.finalize();
}
}
@@ -5190,6 +5017,9 @@
" -- volMax: " + rcse.mPlaybackVolumeMax +
" -- volObs: " + rcse.mRemoteVolumeObs);
}
+ synchronized(mCurrentRcLock) {
+ pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
+ }
}
synchronized (mMainRemote) {
pw.println("\nRemote Volume State:");
@@ -5226,11 +5056,12 @@
* Remove any entry in the remote control stack that has the same package name as packageName
* Pre-condition: packageName != null
*/
- private void removeMediaButtonReceiverForPackage(String packageName) {
+ private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) {
synchronized(mRCStack) {
if (mRCStack.empty()) {
return;
} else {
+ final PackageManager pm = mContext.getPackageManager();
RemoteControlStackEntry oldTop = mRCStack.peek();
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
// iterate over the stack entries
@@ -5238,10 +5069,19 @@
// evaluated it, traversal order doesn't matter here)
while(stackIterator.hasNext()) {
RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
- if (packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
+ if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
// a stack entry is from the package being removed, remove it from the stack
stackIterator.remove();
- rcse.unlinkToRcClientDeath();
+ rcse.destroy();
+ } else if (rcse.mReceiverComponent != null) {
+ try {
+ // Check to see if this receiver still exists.
+ pm.getReceiverInfo(rcse.mReceiverComponent, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Not found -- remove it!
+ stackIterator.remove();
+ rcse.destroy();
+ }
}
}
if (mRCStack.empty()) {
@@ -5273,6 +5113,10 @@
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);
@@ -5280,7 +5124,7 @@
mediaButtonIntent.setComponent(eventReceiver);
PendingIntent pi = PendingIntent.getBroadcast(mContext,
0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
- registerMediaButtonIntent(pi, eventReceiver);
+ registerMediaButtonIntent(pi, eventReceiver, null);
}
}
@@ -5288,9 +5132,10 @@
* 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) {
+ private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
+ IBinder token) {
// already at top of stack?
if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
return;
@@ -5312,13 +5157,15 @@
Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
}
if (!wasInsideStack) {
- rcse = new RemoteControlStackEntry(mediaIntent, target);
+ rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
}
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*/) );
+ }
}
/**
@@ -5332,7 +5179,7 @@
for (int index = mRCStack.size()-1; index >= 0; index--) {
final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
if (rcse.mMediaIntent.equals(pi)) {
- rcse.unlinkToRcClientDeath();
+ rcse.destroy();
// ok to remove element while traversing the stack since we're leaving the loop
mRCStack.removeElementAt(index);
break;
@@ -5618,14 +5465,15 @@
/**
* see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
- * precondition: mediaIntent != null, target != null
+ * precondition: mediaIntent != null
*/
- public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
+ public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
+ IBinder token) {
Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
synchronized(mAudioFocusLock) {
synchronized(mRCStack) {
- pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver);
+ pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token);
// new RC client, assume every type of information shall be queried
checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
}
@@ -5636,7 +5484,7 @@
* 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);
@@ -6018,6 +5866,29 @@
}
}
+ 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 */);
@@ -6391,10 +6262,7 @@
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);
}
@@ -6540,7 +6408,6 @@
private void enforceSafeMediaVolume() {
VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
- boolean lastAudible = (streamState.muteCount() != 0);
int devices = mSafeMediaVolumeDevices;
int i = 0;
@@ -6549,27 +6416,16 @@
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/AudioTrack.java b/media/java/android/media/AudioTrack.java
index cd50de4..399eb7b 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -791,7 +791,7 @@
* {@link #ERROR_INVALID_OPERATION}
*/
public int setPlaybackRate(int sampleRateInHz) {
- if (mState == STATE_UNINITIALIZED) {
+ if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
if (sampleRateInHz <= 0) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index e21b26b..13f6c02 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -120,17 +120,12 @@
oneway void dispatchMediaKeyEvent(in KeyEvent keyEvent);
void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent);
- void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
- oneway void unregisterMediaButtonIntent(in PendingIntent pi, in ComponentName c);
+ void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c, IBinder token);
+ 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,9 +152,29 @@
* 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);
+ 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 @@
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 095cf80..c70889c 100644
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -43,7 +43,16 @@
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..4eb0c56 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -25,11 +25,12 @@
import android.os.Looper;
import android.os.Message;
import android.os.Bundle;
+import android.os.Parcel;
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,9 +132,11 @@
void onEvent(MediaDrm md, byte[] sessionId, int event, int extra, byte[] data);
}
- /* Do not change these values without updating their counterparts
- * in include/media/mediadrm.h!
- */
+ 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;
+
private static final int DRM_EVENT = 200;
private class EventHandler extends Handler
@@ -157,10 +160,18 @@
Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
if (mOnEventListener != null) {
- Bundle bundle = msg.getData();
- byte[] sessionId = bundle.getByteArray("sessionId");
- byte[] data = bundle.getByteArray("data");
- mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ if (sessionId.length == 0) {
+ sessionId = null;
+ }
+ byte[] data = parcel.createByteArray();
+ if (data.length == 0) {
+ data = null;
+ }
+ mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
+ }
}
return;
@@ -179,14 +190,14 @@
* the cookie passed to native_setup().)
*/
private static void postEventFromNative(Object mediadrm_ref,
- int what, int arg1, int arg2, Object obj)
+ int eventType, int extra, Object obj)
{
MediaDrm md = (MediaDrm)((WeakReference)mediadrm_ref).get();
if (md == null) {
return;
}
if (md.mEventHandler != null) {
- Message m = md.mEventHandler.obtainMessage(what, arg1, arg2, obj);
+ Message m = md.mEventHandler.obtainMessage(DRM_EVENT, eventType, extra, obj);
md.mEventHandler.sendMessage(m);
}
}
@@ -197,68 +208,88 @@
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 +300,23 @@
/**
* 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 +346,140 @@
*
* @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 @@
* 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/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 1f5ca35..c0fbd2e 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -257,8 +257,10 @@
}
/**
- * Writes an encoded sample into the muxer. The application needs to make
- * sure that the samples are written into the right tracks.
+ * Writes an encoded sample into the muxer.
+ * <p>The application needs to make sure that the samples are written into
+ * the right tracks. Also, it needs to make sure the samples for each track
+ * are written in chronological order.</p>
* @param byteBuf The encoded sample.
* @param trackIndex The track index for this sample.
* @param bufferInfo The buffer information related to this sample.
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index f186000..e076ef0 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -278,11 +278,14 @@
public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
/**
* @hide
- * (to be un-hidden and added in javadoc of setTransportControlFlags(int))
+ * 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.
- *
+ * 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;
@@ -605,7 +608,7 @@
/**
* @hide
- * (to be un-hidden)
+ * 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:
@@ -630,11 +633,6 @@
*/
public void setPlaybackState(int state, long timeInMs, float playbackSpeed) {
synchronized(mCacheLock) {
- if (timeInMs != PLAYBACK_POSITION_INVALID) {
- mPlaybackPositionCapabilities |= MEDIA_POSITION_READABLE;
- } else {
- mPlaybackPositionCapabilities &= ~MEDIA_POSITION_READABLE;
- }
if ((mPlaybackState != state) || (mPlaybackPositionMs != timeInMs)
|| (mPlaybackSpeed != playbackSpeed)) {
// store locally
@@ -670,19 +668,20 @@
mTransportControlFlags = transportControlFlags;
// send to remote control display if conditions are met
- sendTransportControlFlags_syncCacheLock();
+ sendTransportControlInfo_syncCacheLock();
}
}
/**
* @hide
- * (to be un-hidden)
+ * 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 listener to notify it that the playback head should be set at the given
+ * 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
@@ -694,8 +693,25 @@
/**
* @hide
- * (to be un-hidden)
- * Sets the listener RemoteControlClient calls whenever the media playback position is requested
+ * 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.
@@ -703,16 +719,41 @@
*/
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) {
synchronized(mCacheLock) {
- if ((mPositionUpdateListener == null) && (l != null)) {
+ int oldCapa = mPlaybackPositionCapabilities;
+ if (l != null) {
mPlaybackPositionCapabilities |= MEDIA_POSITION_WRITABLE;
- // tell RCDs and AudioService this RCC accepts position updates
- // TODO implement
- } else if ((mPositionUpdateListener != null) && (l == null)) {
+ } else {
mPlaybackPositionCapabilities &= ~MEDIA_POSITION_WRITABLE;
- // tell RCDs and AudioService this RCC doesn't handle position updates
- // TODO implement
}
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();
+ }
}
}
@@ -896,6 +937,10 @@
*/
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;
@@ -957,14 +1002,14 @@
*/
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);
@@ -1011,6 +1056,16 @@
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)));
+ }
+ }
};
/**
@@ -1051,6 +1106,7 @@
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) {
@@ -1072,7 +1128,7 @@
break;
case MSG_REQUEST_TRANSPORTCONTROL:
synchronized (mCacheLock) {
- sendTransportControlFlags_syncCacheLock();
+ sendTransportControlInfo_syncCacheLock();
}
break;
case MSG_REQUEST_ARTWORK:
@@ -1095,6 +1151,8 @@
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");
}
@@ -1136,14 +1194,14 @@
}
}
- 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);
@@ -1325,6 +1383,14 @@
}
}
+ private void onSeekTo(int generationId, long timeMs) {
+ synchronized (mCacheLock) {
+ if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) {
+ mPositionUpdateListener.onPlaybackPositionUpdate(timeMs);
+ }
+ }
+ }
+
//===========================================================
// Internal utilities
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 9938f76..c32ba9d 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -21,10 +21,12 @@
#include "android_media_MediaDrm.h"
#include "android_runtime/AndroidRuntime.h"
+#include "android_os_Parcel.h"
#include "jni.h"
#include "JNIHelp.h"
#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
#include <media/IDrm.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -43,6 +45,15 @@
var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " fieldName);
+#define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+#define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find static method " fieldName);
+
+
struct RequestFields {
jfieldID data;
jfieldID defaultUrl;
@@ -74,9 +85,17 @@
jmethodID getValue;
};
+struct EventTypes {
+ int kEventProvisionRequired;
+ int kEventKeyRequired;
+ int kEventKeyExpired;
+ int kEventVendorDefined;
+} gEventTypes;
+
struct fields_t {
jfieldID context;
- RequestFields licenseRequest;
+ jmethodID post_event;
+ RequestFields keyRequest;
RequestFields provisionRequest;
ArrayListFields arraylist;
HashmapFields hashmap;
@@ -87,6 +106,88 @@
static fields_t gFields;
+// ----------------------------------------------------------------------------
+// ref-counted object for callbacks
+class JNIDrmListener: public DrmListener
+{
+public:
+ JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
+ ~JNIDrmListener();
+ virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj = NULL);
+private:
+ JNIDrmListener();
+ jclass mClass; // Reference to MediaDrm class
+ jobject mObject; // Weak ref to MediaDrm Java object to call on
+};
+
+JNIDrmListener::JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
+{
+ // Hold onto the MediaDrm class for use in calling the static method
+ // that posts events to the application thread.
+ jclass clazz = env->GetObjectClass(thiz);
+ if (clazz == NULL) {
+ ALOGE("Can't find android/media/MediaDrm");
+ jniThrowException(env, "java/lang/Exception", NULL);
+ return;
+ }
+ mClass = (jclass)env->NewGlobalRef(clazz);
+
+ // We use a weak reference so the MediaDrm object can be garbage collected.
+ // The reference is only used as a proxy for callbacks.
+ mObject = env->NewGlobalRef(weak_thiz);
+}
+
+JNIDrmListener::~JNIDrmListener()
+{
+ // remove global references
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->DeleteGlobalRef(mObject);
+ env->DeleteGlobalRef(mClass);
+}
+
+void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra,
+ const Parcel *obj)
+{
+ jint jeventType;
+
+ // translate DrmPlugin event types into their java equivalents
+ switch(eventType) {
+ case DrmPlugin::kDrmPluginEventProvisionRequired:
+ jeventType = gEventTypes.kEventProvisionRequired;
+ break;
+ case DrmPlugin::kDrmPluginEventKeyNeeded:
+ jeventType = gEventTypes.kEventKeyRequired;
+ break;
+ case DrmPlugin::kDrmPluginEventKeyExpired:
+ jeventType = gEventTypes.kEventKeyExpired;
+ break;
+ case DrmPlugin::kDrmPluginEventVendorDefined:
+ jeventType = gEventTypes.kEventVendorDefined;
+ break;
+ default:
+ ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
+ return;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (obj && obj->dataSize() > 0) {
+ jobject jParcel = createJavaParcelObject(env);
+ if (jParcel != NULL) {
+ Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
+ nativeParcel->setData(obj->data(), obj->dataSize());
+ env->CallStaticVoidMethod(mClass, gFields.post_event, mObject,
+ jeventType, extra, jParcel);
+ }
+ }
+
+ if (env->ExceptionCheck()) {
+ ALOGW("An exception occurred while notifying an event.");
+ LOGW_EX(env);
+ env->ExceptionClear();
+ }
+}
+
+
static bool throwExceptionAsNecessary(
JNIEnv *env, status_t err, const char *msg = NULL) {
@@ -109,6 +210,9 @@
JNIEnv *env, jobject thiz, const uint8_t uuid[16]) {
mObject = env->NewWeakGlobalRef(thiz);
mDrm = MakeDrm(uuid);
+ if (mDrm != NULL) {
+ mDrm->setListener(this);
+ }
}
JDrm::~JDrm() {
@@ -160,6 +264,25 @@
return drm;
}
+status_t JDrm::setListener(const sp<DrmListener>& listener) {
+ Mutex::Autolock lock(mLock);
+ mListener = listener;
+ return OK;
+}
+
+void JDrm::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
+ sp<DrmListener> listener;
+ mLock.lock();
+ listener = mListener;
+ mLock.unlock();
+
+ if (listener != NULL) {
+ Mutex::Autolock lock(mNotifyLock);
+ listener->notify(eventType, extra, obj);
+ }
+}
+
+
// static
bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
sp<IDrm> drm = MakeDrm();
@@ -194,16 +317,16 @@
}
static String8 JStringToString8(JNIEnv *env, jstring const &jstr) {
- jboolean isCopy;
String8 result;
- const char *s = env->GetStringUTFChars(jstr, &isCopy);
+ const char *s = env->GetStringUTFChars(jstr, NULL);
if (s) {
result = s;
env->ReleaseStringUTFChars(jstr, s);
}
return result;
}
+
/*
import java.util.HashMap;
import java.util.Set;
@@ -321,17 +444,32 @@
}
static void android_media_MediaDrm_release(JNIEnv *env, jobject thiz) {
- setDrm(env, thiz, NULL);
+ sp<JDrm> drm = setDrm(env, thiz, NULL);
+ if (drm != NULL) {
+ drm->setListener(NULL);
+ }
}
static void android_media_MediaDrm_native_init(JNIEnv *env) {
jclass clazz;
FIND_CLASS(clazz, "android/media/MediaDrm");
GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "I");
+ GET_STATIC_METHOD_ID(gFields.post_event, clazz, "postEventFromNative",
+ "(Ljava/lang/Object;IILjava/lang/Object;)V");
- 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;");
+ jfieldID field;
+ GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_PROVISION_REQUIRED", "I");
+ gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_REQUIRED", "I");
+ gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_KEY_EXPIRED", "I");
+ gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "MEDIA_DRM_EVENT_VENDOR_DEFINED", "I");
+ gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field);
+
+ 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");
@@ -388,6 +526,8 @@
return;
}
+ sp<JNIDrmListener> listener = new JNIDrmListener(env, thiz, weak_this);
+ drm->setListener(listener);
setDrm(env, thiz, drm);
}
@@ -451,9 +591,9 @@
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 +612,7 @@
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 +622,94 @@
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 +720,9 @@
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 +918,162 @@
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 +1094,21 @@
{ "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 +1133,26 @@
{ "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_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index 01067c4..9b3917f 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -20,6 +20,8 @@
#include "jni.h"
#include <media/stagefright/foundation/ABase.h>
+#include <media/IDrm.h>
+#include <media/IDrmClient.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -27,15 +29,24 @@
struct IDrm;
-struct JDrm : public RefBase {
+class DrmListener: virtual public RefBase
+{
+public:
+ virtual void notify(DrmPlugin::EventType eventType, int extra,
+ const Parcel *obj) = 0;
+};
+
+struct JDrm : public BnDrmClient {
static bool IsCryptoSchemeSupported(const uint8_t uuid[16]);
JDrm(JNIEnv *env, jobject thiz, const uint8_t uuid[16]);
status_t initCheck() const;
-
sp<IDrm> getDrm() { return mDrm; }
+ void notify(DrmPlugin::EventType, int extra, const Parcel *obj);
+ status_t setListener(const sp<DrmListener>& listener);
+
protected:
virtual ~JDrm();
@@ -43,6 +54,10 @@
jweak mObject;
sp<IDrm> mDrm;
+ sp<DrmListener> mListener;
+ Mutex mNotifyLock;
+ Mutex mLock;
+
static sp<IDrm> MakeDrm();
static sp<IDrm> MakeDrm(const uint8_t uuid[16]);
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 @@
int result;
void clearImage() {
- context = null;
image = null;
imageUri = null;
iconSize = 0;
}
+ void clearContext() {
+ context = null;
+ }
}
/**
@@ -172,8 +174,6 @@
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 @@
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 @@
// mounted
params[0].clearImage();
params[0].result = 1;
- Log.d(TAG, "doInBackground failed");
}
// Recycle the bitmap data
@@ -254,7 +253,6 @@
image.recycle();
}
- Log.d(TAG, "doInBackground complete");
return params[0];
}
@@ -263,7 +261,7 @@
if (isCancelled()) {
params.finisher.run();
params.clearImage();
- Log.d(TAG, "onPostExecute cancelled");
+ params.clearContext();
return;
}
@@ -291,7 +289,7 @@
mNotificationManager.notify(mNotificationId, n);
}
params.finisher.run();
- Log.d(TAG, "onPostExecute complete");
+ params.clearContext();
}
}
@@ -395,15 +393,12 @@
// 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 @@
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 @@
* 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 @@
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 @@
if (mScreenBitmap == null) {
notifyScreenshotError(mContext, mNotificationManager);
finisher.run();
- Log.d(TAG, "takeScreenshot null bitmap");
return;
}
@@ -477,7 +466,6 @@
// Recycle the previous bitmap
mScreenBitmap.recycle();
mScreenBitmap = ss;
- Log.d(TAG, "takeScreenshot rotation bitmap created");
}
// Optimizations
@@ -487,7 +475,6 @@
// Start the post-screenshot animation
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
statusBarVisible, navBarVisible);
- Log.d(TAG, "takeScreenshot startedAnimation");
}
@@ -496,7 +483,6 @@
*/
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 @@
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 @@
// Clear any references to the bitmap
mScreenBitmap = null;
mScreenshotView.setImageBitmap(null);
- Log.d(TAG, "startAnimation onAnimationEnd");
}
});
mScreenshotLayout.post(new Runnable() {
@@ -537,7 +520,6 @@
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mScreenshotView.buildLayer();
mScreenshotAnimation.start();
- Log.d(TAG, "startAnimation post runnable");
}
});
}
@@ -675,7 +657,6 @@
}
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 @@
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/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 7bdcf6e..5b911c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -17,11 +17,11 @@
package com.android.systemui.statusbar;
+import android.service.notification.StatusBarNotification;
import android.content.res.Configuration;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.internal.widget.SizeAdaptiveLayout;
import com.android.systemui.R;
import com.android.systemui.SearchPanelView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 752bb0c..cbbaab3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -20,10 +20,10 @@
import android.os.IBinder;
import android.os.Message;
+import android.service.notification.StatusBarNotification;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.statusbar.StatusBarNotification;
/**
* This class takes the functions from IStatusBar that come in on
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index c82f250..886ed77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -16,12 +16,11 @@
package com.android.systemui.statusbar;
-import android.app.Notification;
+import android.service.notification.StatusBarNotification;
import android.os.IBinder;
import android.view.View;
import android.widget.ImageView;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
import java.util.Comparator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9f54573..c2dc159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -26,6 +26,7 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -76,7 +77,6 @@
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -708,6 +708,7 @@
private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
public void onClick(View v) {
+ awakenDreams();
toggleRecentApps();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
index ecc70d6..976dd01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.service.notification.StatusBarNotification;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -23,10 +24,7 @@
import android.text.StaticLayout;
import android.text.Layout.Alignment;
import android.text.TextPaint;
-import android.text.TextUtils;
-import android.util.Slog;
import android.view.View;
-import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.TextSwitcher;
import android.widget.TextView;
@@ -35,7 +33,6 @@
import java.util.ArrayList;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.internal.util.CharSequences;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index 0944b40..68d048d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -28,16 +28,11 @@
import android.location.LocationManager;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.Slog;
-import android.view.View;
-import android.widget.ImageView;
// private NM API
import android.app.INotificationManager;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
public class LocationController extends BroadcastReceiver {
private static final String TAG = "StatusBar.LocationController";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 3d6bfe7..05bba89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -23,6 +23,7 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -58,7 +59,6 @@
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
index 0859874..725d9e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -21,9 +21,9 @@
import android.animation.LayoutTransition;
import android.app.Notification;
import android.app.PendingIntent;
+import android.service.notification.StatusBarNotification;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -37,11 +37,9 @@
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarIconView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 413cc78..dc5de02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.tv;
+import android.service.notification.StatusBarNotification;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.statusbar.BaseStatusBar;
import android.os.IBinder;
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 ad5e20b..6b28e8e 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -380,6 +380,15 @@
st.createdPanelView = cb.onCreatePanelView(st.featureId);
}
+ final boolean isActionBarMenu =
+ (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR);
+
+ if (isActionBarMenu && mActionBar != null) {
+ // Enforce ordering guarantees around events so that the action bar never
+ // dispatches menu-related events before the panel is prepared.
+ mActionBar.setMenuPrepared();
+ }
+
if (st.createdPanelView == null) {
// Init the panel state's menu--return false if init failed
if (st.menu == null || st.refreshMenuContent) {
@@ -389,7 +398,7 @@
}
}
- if (mActionBar != null) {
+ if (isActionBarMenu && mActionBar != null) {
if (mActionMenuPresenterCallback == null) {
mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
}
@@ -405,7 +414,7 @@
// Ditch the menu created above
st.setMenu(null);
- if (mActionBar != null) {
+ if (isActionBarMenu && mActionBar != null) {
// Don't show it in the action bar either
mActionBar.setMenu(null, mActionMenuPresenterCallback);
}
@@ -430,7 +439,7 @@
}
if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) {
- if (mActionBar != null) {
+ if (isActionBarMenu && mActionBar != null) {
// The app didn't want to show the menu for now but it still exists.
// Clear it out of the action bar.
mActionBar.setMenu(null, mActionMenuPresenterCallback);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index bb05325..5f9e921 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -59,6 +59,8 @@
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -441,6 +443,9 @@
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 +903,8 @@
mScreenshotChordEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableScreenshotChord);
+ mGlobalKeyManager = new GlobalKeyManager(mContext);
+
// Controls rotation and the like.
initializeHdmiState();
@@ -1796,7 +1803,23 @@
? com.android.internal.R.anim.lock_screen_wallpaper_behind_enter
: com.android.internal.R.anim.lock_screen_behind_enter);
}
-
+
+ private static void awakenDreams() {
+ IDreamManager dreamManager = getDreamManager();
+ if (dreamManager != null) {
+ try {
+ dreamManager.awaken();
+ } catch (RemoteException e) {
+ // fine, stay asleep then
+ }
+ }
+ }
+
+ static IDreamManager getDreamManager() {
+ return IDreamManager.Stub.asInterface(
+ ServiceManager.checkService(DreamService.DREAM_SERVICE));
+ }
+
static ITelephony getTelephonyService() {
return ITelephony.Stub.asInterface(
ServiceManager.checkService(Context.TELEPHONY_SERVICE));
@@ -2140,6 +2163,10 @@
return -1;
}
+ if (mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
+ return -1;
+ }
+
// Let the application handle the key.
return 0;
}
@@ -3585,6 +3612,12 @@
}
}
+ // 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:
@@ -4535,6 +4568,7 @@
}
void startDockOrHome() {
+ awakenDreams();
// We don't have dock home anymore. Home is home. If you lived here, you'd be home by now.
mContext.startActivityAsUser(mHomeIntent, UserHandle.CURRENT);
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 d5798d7..5e3b7da 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -147,7 +147,7 @@
}
}
- 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 e958e9a..159a92d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -211,7 +211,7 @@
}
- public void setTransportControlFlags(int generationId, int flags) {
+ public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
}
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 43d76bb..602afd4 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -882,8 +882,9 @@
snprintf(name, sizeof(name), "%d", range.axis);
}
dump.appendFormat(INDENT3 "%s: source=0x%08x, "
- "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
- name, range.source, range.min, range.max, range.flat, range.fuzz);
+ "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
+ name, range.source, range.min, range.max, range.flat, range.fuzz,
+ range.resolution);
}
}
@@ -2247,20 +2248,20 @@
if (mParameters.mode == Parameters::MODE_POINTER) {
float minX, minY, maxX, maxY;
if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
- info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f);
- info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
}
} else {
- info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale);
- info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale);
+ info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
}
- info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
if (mCursorScrollAccumulator.haveRelativeVWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
}
if (mCursorScrollAccumulator.haveRelativeHWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
}
}
@@ -2611,10 +2612,12 @@
}
if (mCursorScrollAccumulator.haveRelativeVWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
}
if (mCursorScrollAccumulator.haveRelativeHWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
}
}
}
@@ -3063,6 +3066,7 @@
mOrientedRanges.touchMajor.max = diagonalSize;
mOrientedRanges.touchMajor.flat = 0;
mOrientedRanges.touchMajor.fuzz = 0;
+ mOrientedRanges.touchMajor.resolution = 0;
mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
@@ -3073,6 +3077,7 @@
mOrientedRanges.toolMajor.max = diagonalSize;
mOrientedRanges.toolMajor.flat = 0;
mOrientedRanges.toolMajor.fuzz = 0;
+ mOrientedRanges.toolMajor.resolution = 0;
mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
@@ -3083,6 +3088,7 @@
mOrientedRanges.size.max = 1.0;
mOrientedRanges.size.flat = 0;
mOrientedRanges.size.fuzz = 0;
+ mOrientedRanges.size.resolution = 0;
} else {
mSizeScale = 0.0f;
}
@@ -3106,6 +3112,7 @@
mOrientedRanges.pressure.max = 1.0;
mOrientedRanges.pressure.flat = 0;
mOrientedRanges.pressure.fuzz = 0;
+ mOrientedRanges.pressure.resolution = 0;
// Tilt
mTiltXCenter = 0;
@@ -3129,6 +3136,7 @@
mOrientedRanges.tilt.max = M_PI_2;
mOrientedRanges.tilt.flat = 0;
mOrientedRanges.tilt.fuzz = 0;
+ mOrientedRanges.tilt.resolution = 0;
}
// Orientation
@@ -3142,6 +3150,7 @@
mOrientedRanges.orientation.max = M_PI;
mOrientedRanges.orientation.flat = 0;
mOrientedRanges.orientation.fuzz = 0;
+ mOrientedRanges.orientation.resolution = 0;
} else if (mCalibration.orientationCalibration !=
Calibration::ORIENTATION_CALIBRATION_NONE) {
if (mCalibration.orientationCalibration
@@ -3165,6 +3174,7 @@
mOrientedRanges.orientation.max = M_PI_2;
mOrientedRanges.orientation.flat = 0;
mOrientedRanges.orientation.fuzz = 0;
+ mOrientedRanges.orientation.resolution = 0;
}
// Distance
@@ -3190,6 +3200,7 @@
mOrientedRanges.distance.flat = 0;
mOrientedRanges.distance.fuzz =
mRawPointerAxes.distance.fuzz * mDistanceScale;
+ mOrientedRanges.distance.resolution = 0;
}
// Compute oriented precision, scales and ranges.
@@ -3204,12 +3215,14 @@
mOrientedRanges.x.min = mYTranslate;
mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = mYScale;
+ mOrientedRanges.x.fuzz = 0;
+ mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
mOrientedRanges.y.min = mXTranslate;
mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = mXScale;
+ mOrientedRanges.y.fuzz = 0;
+ mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
break;
default:
@@ -3219,12 +3232,14 @@
mOrientedRanges.x.min = mXTranslate;
mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
mOrientedRanges.x.flat = 0;
- mOrientedRanges.x.fuzz = mXScale;
+ mOrientedRanges.x.fuzz = 0;
+ mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
mOrientedRanges.y.min = mYTranslate;
mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
mOrientedRanges.y.flat = 0;
- mOrientedRanges.y.fuzz = mYScale;
+ mOrientedRanges.y.fuzz = 0;
+ mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
break;
}
@@ -6045,10 +6060,10 @@
for (size_t i = 0; i < mAxes.size(); i++) {
const Axis& axis = mAxes.valueAt(i);
info->addMotionRange(axis.axisInfo.axis, AINPUT_SOURCE_JOYSTICK,
- axis.min, axis.max, axis.flat, axis.fuzz);
+ axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
info->addMotionRange(axis.axisInfo.highAxis, AINPUT_SOURCE_JOYSTICK,
- axis.min, axis.max, axis.flat, axis.fuzz);
+ axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
}
}
}
@@ -6078,8 +6093,8 @@
dump.append(" (invert)");
}
- dump.appendFormat(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f\n",
- axis.min, axis.max, axis.flat, axis.fuzz);
+ dump.appendFormat(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n",
+ axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
dump.appendFormat(INDENT4 " scale=%0.5f, offset=%0.5f, "
"highScale=%0.5f, highOffset=%0.5f\n",
axis.scale, axis.offset, axis.highScale, axis.highOffset);
@@ -6125,18 +6140,21 @@
float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
scale, 0.0f, highScale, 0.0f,
- 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+ 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+ rawAxisInfo.resolution * scale);
} else if (isCenteredAxis(axisInfo.axis)) {
float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
scale, offset, scale, offset,
- -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+ -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+ rawAxisInfo.resolution * scale);
} else {
float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
scale, 0.0f, scale, 0.0f,
- 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+ 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+ rawAxisInfo.resolution * scale);
}
// To eliminate noise while the joystick is at rest, filter out small variations
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index c596b37..8a52c06 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -1730,10 +1730,11 @@
float highScale; // scale factor from raw to normalized values of high split
float highOffset; // offset to add after scaling for normalization of high split
- float min; // normalized inclusive minimum
- float max; // normalized inclusive maximum
- float flat; // normalized flat region size
- float fuzz; // normalized error tolerance
+ float min; // normalized inclusive minimum
+ float max; // normalized inclusive maximum
+ float flat; // normalized flat region size
+ float fuzz; // normalized error tolerance
+ float resolution; // normalized resolution in units/mm
float filter; // filter out small variations of this size
float currentValue; // current value
@@ -1744,7 +1745,7 @@
void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
bool explicitlyMapped, float scale, float offset,
float highScale, float highOffset,
- float min, float max, float flat, float fuzz) {
+ float min, float max, float flat, float fuzz, float resolution) {
this->rawAxisInfo = rawAxisInfo;
this->axisInfo = axisInfo;
this->explicitlyMapped = explicitlyMapped;
@@ -1756,6 +1757,7 @@
this->max = max;
this->flat = flat;
this->fuzz = fuzz;
+ this->resolution = resolution;
this->filter = 0;
resetValue();
}
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 8bc2da2..59577ad 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -157,3 +157,8 @@
# ConfigUpdateInstallReceiver.java
# ---------------------------
51300 config_install_failed (dir|3)
+
+# ---------------------------
+# IntentFirewall.java
+# ---------------------------
+51400 ifw_intent_matched (Intent Type|1|5),(Component Name|3),(Caller Uid|1|5),(Caller Pkg Count|1|1),(Caller Pkgs|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5)
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 3b541ec..a28c387 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1070,7 +1070,8 @@
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
- return new InputBindResult(session.session, session.channel, mCurId, mCurSeq);
+ return new InputBindResult(session.session,
+ session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq);
}
InputBindResult startInputLocked(IInputMethodClient client,
@@ -2357,13 +2358,15 @@
return true;
case MSG_CREATE_SESSION: {
args = (SomeArgs)msg.obj;
+ IInputMethod method = (IInputMethod)args.arg1;
InputChannel channel = (InputChannel)args.arg2;
try {
- ((IInputMethod)args.arg1).createSession(channel,
- (IInputSessionCallback)args.arg3);
+ method.createSession(channel, (IInputSessionCallback)args.arg3);
} catch (RemoteException e) {
} finally {
- if (channel != null) {
+ // Dispose the channel if the input method is not local to this process
+ // because the remote proxy will get its own copy when unparceled.
+ if (channel != null && Binder.isProxy(method)) {
channel.dispose();
}
}
@@ -2404,16 +2407,24 @@
// There is nothing interesting about the last client dying.
}
return true;
- case MSG_BIND_METHOD:
+ case MSG_BIND_METHOD: {
args = (SomeArgs)msg.obj;
+ IInputMethodClient client = (IInputMethodClient)args.arg1;
+ InputBindResult res = (InputBindResult)args.arg2;
try {
- ((IInputMethodClient)args.arg1).onBindMethod(
- (InputBindResult)args.arg2);
+ client.onBindMethod(res);
} catch (RemoteException e) {
Slog.w(TAG, "Client died receiving input method " + args.arg2);
+ } finally {
+ // Dispose the channel if the input method is not local to this process
+ // because the remote proxy will get its own copy when unparceled.
+ if (res.channel != null && Binder.isProxy(client)) {
+ res.channel.dispose();
+ }
}
args.recycle();
return true;
+ }
case MSG_SET_ACTIVE:
try {
((ClientState)msg.obj).client.setActive(msg.arg1 != 0);
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 9b19008..35345f5 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -117,7 +117,7 @@
boolean printedHeader = false;
F filter;
for (int i=0; i<N && (filter=a[i]) != null; i++) {
- if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
continue;
}
if (title != null) {
@@ -357,11 +357,11 @@
}
/**
- * Return the package that owns this filter. This must be implemented to
- * provide correct filtering of Intents that have specified a package name
- * they are to be delivered to.
+ * Returns whether this filter is owned by this package. This must be
+ * implemented to provide correct filtering of Intents that have
+ * specified a package name they are to be delivered to.
*/
- protected abstract String packageForFilter(F filter);
+ protected abstract boolean isPackageForFilter(String packageName, F filter);
protected abstract F[] newArray(int size);
@@ -556,7 +556,7 @@
}
// Is delivery being limited to filters owned by a particular package?
- if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
if (debug) {
Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
}
@@ -710,8 +710,8 @@
}
private final IntentResolverOld<F, R> mOldResolver = new IntentResolverOld<F, R>() {
- @Override protected String packageForFilter(F filter) {
- return IntentResolver.this.packageForFilter(filter);
+ @Override protected boolean isPackageForFilter(String packageName, F filter) {
+ return IntentResolver.this.isPackageForFilter(packageName, filter);
}
@Override protected boolean allowFilterResult(F filter, List<R> dest) {
return IntentResolver.this.allowFilterResult(filter, dest);
diff --git a/services/java/com/android/server/IntentResolverOld.java b/services/java/com/android/server/IntentResolverOld.java
index 4dd77ce..94a2379 100644
--- a/services/java/com/android/server/IntentResolverOld.java
+++ b/services/java/com/android/server/IntentResolverOld.java
@@ -106,7 +106,7 @@
boolean printedHeader = false;
for (int i=0; i<N; i++) {
F filter = a.get(i);
- if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
continue;
}
if (title != null) {
@@ -336,11 +336,11 @@
}
/**
- * Return the package that owns this filter. This must be implemented to
- * provide correct filtering of Intents that have specified a package name
- * they are to be delivered to.
+ * Returns whether this filter is owned by this package. This must be
+ * implemented to provide correct filtering of Intents that have
+ * specified a package name they are to be delivered to.
*/
- protected abstract String packageForFilter(F filter);
+ protected abstract boolean isPackageForFilter(String packageName, F filter);
@SuppressWarnings("unchecked")
protected R newResult(F filter, int match, int userId) {
@@ -529,7 +529,7 @@
}
// Is delivery being limited to filters owned by a particular package?
- if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
if (debug) {
Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 5cf1c28..cfb892f 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -26,16 +26,17 @@
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
-import android.app.INotificationListener;
import android.app.ITransientNotification;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -57,6 +58,9 @@
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
+import android.service.notification.INotificationListener;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.AtomicFile;
@@ -68,8 +72,6 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
-import com.android.internal.statusbar.StatusBarNotification;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -121,6 +123,8 @@
private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true;
private static final boolean ENABLE_BLOCKED_TOASTS = true;
+ private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":";
+
final Context mContext;
final IActivityManager mAm;
final UserManager mUserManager;
@@ -163,8 +167,18 @@
private final AppOpsManager mAppOps;
- private ArrayList<NotificationListenerInfo> mListeners = new ArrayList<NotificationListenerInfo>();
- private ArrayList<String> mEnabledListenersForCurrentUser = new ArrayList<String>();
+ // contains connections to all connected listeners, including app services
+ // and system listeners
+ private ArrayList<NotificationListenerInfo> mListeners
+ = new ArrayList<NotificationListenerInfo>();
+ // things that will be put into mListeners as soon as they're ready
+ private ArrayList<String> mServicesBinding = new ArrayList<String>();
+ // lists the component names of all enabled (and therefore connected) listener
+ // app services for the current user only
+ private HashSet<ComponentName> mEnabledListenersForCurrentUser
+ = new HashSet<ComponentName>();
+ // Just the packages from mEnabledListenersForCurrentUser
+ private HashSet<String> mEnabledListenerPackageNames = new HashSet<String>();
// Notification control database. For now just contains disabled packages.
private AtomicFile mPolicyFile;
@@ -181,27 +195,42 @@
private class NotificationListenerInfo implements DeathRecipient {
INotificationListener listener;
- String pkg;
+ ComponentName component;
int userid;
boolean isSystem;
+ ServiceConnection connection;
- public NotificationListenerInfo(INotificationListener listener, String pkg, int userid,
- boolean isSystem) {
+ public NotificationListenerInfo(INotificationListener listener, ComponentName component,
+ int userid, boolean isSystem) {
this.listener = listener;
- this.pkg = pkg;
+ this.component = component;
this.userid = userid;
this.isSystem = isSystem;
+ this.connection = null;
+ }
+
+ public NotificationListenerInfo(INotificationListener listener, ComponentName component,
+ int userid, ServiceConnection connection) {
+ this.listener = listener;
+ this.component = component;
+ this.userid = userid;
+ this.isSystem = false;
+ this.connection = connection;
}
boolean enabledAndUserMatches(StatusBarNotification sbn) {
final int nid = sbn.getUserId();
- if (!(isSystem || isEnabledForUser(nid))) return false;
+ if (!isEnabledForCurrentUser()) {
+ return false;
+ }
if (this.userid == UserHandle.USER_ALL) return true;
return (nid == UserHandle.USER_ALL || nid == this.userid);
}
public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
- if (!enabledAndUserMatches(sbn)) return;
+ if (!enabledAndUserMatches(sbn)) {
+ return;
+ }
try {
listener.onNotificationPosted(sbn);
} catch (RemoteException ex) {
@@ -220,15 +249,17 @@
@Override
public void binderDied() {
- unregisterListener(this.listener, this.userid);
+ if (connection == null) {
+ // This is not a service; it won't be recreated. We can give up this connection.
+ unregisterListener(this.listener, this.userid);
+ }
}
/** convenience method for looking in mEnabledListenersForCurrentUser */
- public boolean isEnabledForUser(int userid) {
- for (int i=0; i<mEnabledListenersForCurrentUser.size(); i++) {
- if (this.pkg.equals(mEnabledListenersForCurrentUser.get(i))) return true;
- }
- return false;
+ public boolean isEnabledForCurrentUser() {
+ if (this.isSystem) return true;
+ if (this.connection == null) return false;
+ return mEnabledListenersForCurrentUser.contains(this.component);
}
}
@@ -237,9 +268,15 @@
ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
public Archive() {
-
}
+
public void record(StatusBarNotification nr) {
+ // Nuke heavy parts of notification before storing in archive
+ nr.notification.tickerView = null;
+ nr.notification.contentView = null;
+ nr.notification.bigContentView = null;
+ nr.notification.largeIcon = null;
+
if (mBuffer.size() == BUFFER_SIZE) {
mBuffer.removeFirst();
}
@@ -428,6 +465,12 @@
}
}
+ /**
+ * System-only API for getting a list of current (i.e. not cleared) notifications.
+ *
+ * Requires ACCESS_NOTIFICATIONS which is signature|system.
+ */
+ @Override
public StatusBarNotification[] getActiveNotifications(String callingPkg) {
// enforce() will ensure the calling uid has the correct permission
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -450,6 +493,12 @@
return tmp;
}
+ /**
+ * System-only API for getting a list of recent (cleared, no longer shown) notifications.
+ *
+ * Requires ACCESS_NOTIFICATIONS which is signature|system.
+ */
+ @Override
public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
// enforce() will ensure the calling uid has the correct permission
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -468,27 +517,76 @@
return tmp;
}
- boolean packageCanTapNotificationsForUser(final int uid, final String pkg) {
- // Make sure the package and uid match, and that the package is allowed access
- return (AppOpsManager.MODE_ALLOWED
- == mAppOps.checkOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, pkg));
+ /**
+ * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS
+ * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
+ */
+ void rebindListenerServices() {
+ String flat = Settings.Secure.getString(
+ mContext.getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
+ NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
+ final ArrayList<ComponentName> toAdd;
+ final int currentUser = ActivityManager.getCurrentUser();
+
+ synchronized (mNotificationList) {
+ // unbind and remove all existing listeners
+ toRemove = mListeners.toArray(toRemove);
+
+ toAdd = new ArrayList<ComponentName>();
+ final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>();
+ final HashSet<String> newPackages = new HashSet<String>();
+
+ // decode the list of components
+ if (flat != null) {
+ String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
+ for (int i=0; i<components.length; i++) {
+ final ComponentName component
+ = ComponentName.unflattenFromString(components[i]);
+ if (component != null) {
+ newEnabled.add(component);
+ toAdd.add(component);
+ newPackages.add(component.getPackageName());
+ }
+ }
+
+ mEnabledListenersForCurrentUser = newEnabled;
+ mEnabledListenerPackageNames = newPackages;
+ }
+ }
+
+ for (NotificationListenerInfo info : toRemove) {
+ final ComponentName component = info.component;
+ final int oldUser = info.userid;
+ Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component);
+ unregisterListenerService(component, info.userid);
+ }
+
+ final int N = toAdd.size();
+ for (int i=0; i<N; i++) {
+ final ComponentName component = toAdd.get(i);
+ Slog.v(TAG, "enabling notification listener for user " + currentUser + ": "
+ + component);
+ registerListenerService(component, currentUser);
+ }
}
+ /**
+ * Register a listener binder directly with the notification manager.
+ *
+ * Only works with system callers. Apps should extend
+ * {@link android.service.notification.NotificationListenerService}.
+ */
@Override
public void registerListener(final INotificationListener listener,
- final String pkg, final int userid) {
- // ensure system or allowed pkg
- int uid = Binder.getCallingUid();
- boolean isSystem = (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0);
- if (!(isSystem || packageCanTapNotificationsForUser(uid, pkg))) {
- throw new SecurityException("Package " + pkg
- + " may not listen for notifications");
- }
+ final ComponentName component, final int userid) {
+ checkCallerIsSystem();
synchronized (mNotificationList) {
try {
NotificationListenerInfo info
- = new NotificationListenerInfo(listener, pkg, userid, isSystem);
+ = new NotificationListenerInfo(listener, component, userid, true);
listener.asBinder().linkToDeath(info, 0);
mListeners.add(info);
} catch (RemoteException e) {
@@ -497,6 +595,90 @@
}
}
+ /**
+ * Version of registerListener that takes the name of a
+ * {@link android.service.notification.NotificationListenerService} to bind to.
+ *
+ * This is the mechanism by which third parties may subscribe to notifications.
+ */
+ private void registerListenerService(final ComponentName name, final int userid) {
+ checkCallerIsSystem();
+
+ if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid);
+
+ synchronized (mNotificationList) {
+ final String servicesBindingTag = name.toString() + "/" + userid;
+ if (mServicesBinding.contains(servicesBindingTag)) {
+ // stop registering this thing already! we're working on it
+ return;
+ }
+ mServicesBinding.add(servicesBindingTag);
+
+ final int N = mListeners.size();
+ for (int i=N-1; i>=0; i--) {
+ final NotificationListenerInfo info = mListeners.get(i);
+ if (name.equals(info.component)
+ && info.userid == userid) {
+ // cut old connections
+ if (DBG) Slog.v(TAG, " disconnecting old listener: " + info.listener);
+ mListeners.remove(i);
+ if (info.connection != null) {
+ mContext.unbindService(info.connection);
+ }
+ }
+ }
+
+ Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
+ intent.setComponent(name);
+
+ intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.notification_listener_binding_label);
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0));
+
+ try {
+ if (DBG) Slog.v(TAG, "binding: " + intent);
+ if (!mContext.bindServiceAsUser(intent,
+ new ServiceConnection() {
+ INotificationListener mListener;
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mNotificationList) {
+ mServicesBinding.remove(servicesBindingTag);
+ try {
+ mListener = INotificationListener.Stub.asInterface(service);
+ NotificationListenerInfo info = new NotificationListenerInfo(
+ mListener, name, userid, this);
+ service.linkToDeath(info, 0);
+ mListeners.add(info);
+ } catch (RemoteException e) {
+ // already dead
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Slog.v(TAG, "notification listener connection lost: " + name);
+ }
+ },
+ Context.BIND_AUTO_CREATE,
+ new UserHandle(userid)))
+ {
+ mServicesBinding.remove(servicesBindingTag);
+ Slog.w(TAG, "Unable to bind listener service: " + intent);
+ return;
+ }
+ } catch (SecurityException ex) {
+ Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Remove a listener binder directly
+ */
@Override
public void unregisterListener(INotificationListener listener, int userid) {
// no need to check permissions; if your listener binder is in the list,
@@ -507,12 +689,39 @@
for (int i=N-1; i>=0; i--) {
final NotificationListenerInfo info = mListeners.get(i);
if (info.listener == listener && info.userid == userid) {
- mListeners.remove(listener);
+ mListeners.remove(i);
+ if (info.connection != null) {
+ mContext.unbindService(info.connection);
+ }
}
}
}
}
+ /**
+ * Remove a listener service for the given user by ComponentName
+ */
+ private void unregisterListenerService(ComponentName name, int userid) {
+ checkCallerIsSystem();
+
+ synchronized (mNotificationList) {
+ final int N = mListeners.size();
+ for (int i=N-1; i>=0; i--) {
+ final NotificationListenerInfo info = mListeners.get(i);
+ if (name.equals(info.component)
+ && info.userid == userid) {
+ mListeners.remove(i);
+ if (info.connection != null) {
+ mContext.unbindService(info.connection);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * asynchronously notify all listeners about a new notification
+ */
private void notifyPostedLocked(NotificationRecord n) {
final StatusBarNotification sbn = n.sbn;
for (final NotificationListenerInfo info : mListeners) {
@@ -524,6 +733,9 @@
}
}
+ /**
+ * asynchronously notify all listeners about a removed notification
+ */
private void notifyRemovedLocked(NotificationRecord n) {
final StatusBarNotification sbn = n.sbn;
for (final NotificationListenerInfo info : mListeners) {
@@ -535,6 +747,57 @@
}
}
+ // -- APIs to support listeners clicking/clearing notifications --
+
+ private NotificationListenerInfo checkListenerToken(INotificationListener listener) {
+ final IBinder token = listener.asBinder();
+ final int N = mListeners.size();
+ for (int i=0; i<N; i++) {
+ final NotificationListenerInfo info = mListeners.get(i);
+ if (info.listener.asBinder() == token) return info;
+ }
+ throw new SecurityException("Disallowed call from unknown listener: " + listener);
+ }
+
+ /**
+ * Allow an INotificationListener to simulate a "clear all" operation.
+ *
+ * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications}
+ *
+ * @param token The binder for the listener, to check that the caller is allowed
+ */
+ public void clearAllNotificationsFromListener(INotificationListener token) {
+ NotificationListenerInfo info = checkListenerToken(token);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ cancelAll(info.userid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
+ *
+ * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
+ *
+ * @param token The binder for the listener, to check that the caller is allowed
+ */
+ public void clearNotificationFromListener(INotificationListener token, String pkg, String tag, int id) {
+ NotificationListenerInfo info = checkListenerToken(token);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ cancelNotification(pkg, tag, id, 0,
+ Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+ true,
+ info.userid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ // -- end of listener APIs --
+
public static final class NotificationRecord
{
final StatusBarNotification sbn;
@@ -753,12 +1016,23 @@
}
pkgList = new String[]{pkgName};
}
+
+ boolean anyListenersInvolved = false;
if (pkgList != null && (pkgList.length > 0)) {
for (String pkgName : pkgList) {
cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
UserHandle.USER_ALL);
+ if (mEnabledListenerPackageNames.contains(pkgName)) {
+ anyListenersInvolved = true;
+ }
}
}
+
+ if (anyListenersInvolved) {
+ // make sure we're still bound to any of our
+ // listeners who may have just upgraded
+ rebindListenerServices();
+ }
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
// Keep track of screen on/off state, but do not turn off the notification light
// until user passes through the lock screen or views the notification.
@@ -789,7 +1063,7 @@
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
- = Settings.System.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+ = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
SettingsObserver(Handler handler) {
super(handler);
@@ -798,9 +1072,9 @@
void observe() {
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
- false, this);
+ false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
- false, this);
+ false, this, UserHandle.USER_ALL);
update(null);
}
@@ -819,19 +1093,7 @@
}
}
if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
- String pkglist = Settings.Secure.getString(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
- mEnabledListenersForCurrentUser.clear();
- if (pkglist != null) {
- String[] pkgs = pkglist.split(";");
- for (int i=0; i<pkgs.length; i++) {
- final String pkg = pkgs[i];
- if (pkg != null && ! "".equals(pkg)) {
- mEnabledListenersForCurrentUser.add(pkgs[i]);
- }
- }
- }
+ rebindListenerServices();
}
}
}
@@ -950,6 +1212,9 @@
// no beeping until we're basically done booting
mSystemReady = true;
+
+ // make sure our listener services are properly bound
+ rebindListenerServices();
}
// Toasts
@@ -1775,16 +2040,17 @@
pw.println("Current Notification Manager state:");
- pw.print(" Enabled listeners: [");
- for (String pkg : mEnabledListenersForCurrentUser) {
- pw.print(" " + pkg);
+ pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size()
+ + ") enabled for current user:");
+ for (ComponentName cmpt : mEnabledListenersForCurrentUser) {
+ pw.println(" " + cmpt);
}
- pw.println(" ]");
- pw.println(" Live listeners:");
+ pw.println(" Live listeners (" + mListeners.size() + "):");
for (NotificationListenerInfo info : mListeners) {
- pw.println(" " + info.pkg + " (user " + info.userid + "): " + info.listener
- + (info.isSystem?" SYSTEM":""));
+ pw.println(" " + info.component
+ + " (user " + info.userid + "): " + info.listener
+ + (info.isSystem?" SYSTEM":""));
}
int N;
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 1fe98af..c21d8c6 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.StatusBarManager;
+import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -33,7 +34,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.statusbar.StatusBarNotification;
import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
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 @@
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 @@
AlarmManagerService mAlarm;
ActivityManagerService mActivity;
boolean mCompleted;
- boolean mForceKillSystem;
Monitor mCurrentMonitor;
int mPhonePid;
@@ -135,7 +134,9 @@
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 @@
mCompleted = false;
mHandler.sendEmptyMessage(MONITOR);
+
+ final String name;
synchronized (this) {
long timeout = TIME_TO_WAIT;
@@ -396,16 +399,16 @@
// 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 @@
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 eb144ab..14d808f 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -716,7 +716,7 @@
* @param account the account to share with limited users
*/
private void addAccountToLimitedUsers(Account account) {
- List<UserInfo> users = mUserManager.getUsers();
+ List<UserInfo> users = getUserManager().getUsers();
for (UserInfo user : users) {
if (user.isRestricted()) {
addSharedAccountAsUser(account, user.id);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 88ef884..7710f13 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -30,6 +30,7 @@
import com.android.server.SystemServer;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
@@ -274,6 +275,8 @@
public ActivityStack mMainStack;
+ public IntentFirewall mIntentFirewall;
+
private final boolean mHeadless;
// Whether we should show our dialogs (ANR, crash, etc) or just perform their
@@ -570,8 +573,8 @@
}
@Override
- protected String packageForFilter(BroadcastFilter filter) {
- return filter.packageName;
+ protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
+ return packageName.equals(filter.packageName);
}
};
@@ -1407,7 +1410,7 @@
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));
@@ -1445,6 +1448,11 @@
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();
@@ -1467,7 +1475,8 @@
m.mContext = context;
m.mFactoryTest = factoryTest;
m.mMainStack = new ActivityStack(m, context, true, thr.mLooper);
-
+ m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());
+
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
m.mAppOpsService.publish(context);
@@ -4943,6 +4952,14 @@
}
}
+ class IntentFirewallInterface implements IntentFirewall.AMSInterface {
+ public int checkComponentPermission(String permission, int pid, int uid,
+ int owningUid, boolean exported) {
+ return ActivityManagerService.this.checkComponentPermission(permission, pid, uid,
+ owningUid, exported);
+ }
+ }
+
/**
* This can be called with or without the global lock held.
*/
@@ -8333,6 +8350,14 @@
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,
@@ -8846,7 +8871,7 @@
return null;
}
- if (!r.crashing && !r.notResponding) {
+ if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
return null;
}
@@ -8857,7 +8882,7 @@
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) {
@@ -10867,7 +10892,7 @@
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/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 526b24f..3d2e912 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2489,6 +2489,7 @@
int err = ActivityManager.START_SUCCESS;
ProcessRecord callerApp = null;
+
if (caller != null) {
callerApp = mService.getRecordForAppLocked(caller);
if (callerApp != null) {
@@ -2592,34 +2593,36 @@
throw new SecurityException(msg);
}
+ boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
+ callerApp==null?null:callerApp.info, callingUid, callingPid, resolvedType, aInfo);
+
if (mMainStack) {
if (mService.mController != null) {
- boolean abort = false;
try {
// The Intent we give to the watcher has the extra data
// stripped off, since it can contain private information.
Intent watchIntent = intent.cloneFilter();
- abort = !mService.mController.activityStarting(watchIntent,
+ abort |= !mService.mController.activityStarting(watchIntent,
aInfo.applicationInfo.packageName);
} catch (RemoteException e) {
mService.mController = null;
}
-
- if (abort) {
- if (resultRecord != null) {
- sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- // We pretend to the caller that it was really started, but
- // they will just get a cancel result.
- mDismissKeyguardOnNextActivity = false;
- ActivityOptions.abort(options);
- return ActivityManager.START_SUCCESS;
- }
}
}
+ if (abort) {
+ if (resultRecord != null) {
+ sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ // We pretend to the caller that it was really started, but
+ // they will just get a cancel result.
+ mDismissKeyguardOnNextActivity = false;
+ ActivityOptions.abort(options);
+ return ActivityManager.START_SUCCESS;
+ }
+
ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid, callingPackage,
intent, resolvedType, aInfo, mService.mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified);
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/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 @@
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/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 8ff1c7d..fccaab5 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -376,37 +376,37 @@
// icon, but this used to be able to slip through, so for
// those dirty apps give it the app's icon.
foregroundNoti.icon = appInfo.icon;
- if (foregroundNoti.contentView == null) {
- // In this case the app may not have specified a
- // content view... so we'll give them something to show.
- CharSequence appName = appInfo.loadLabel(
- ams.mContext.getPackageManager());
- if (appName == null) {
- appName = appInfo.packageName;
- }
- Context ctx = null;
- try {
- ctx = ams.mContext.createPackageContext(
- appInfo.packageName, 0);
- Intent runningIntent = new Intent(
- Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- runningIntent.setData(Uri.fromParts("package",
- appInfo.packageName, null));
- PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
- runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- foregroundNoti.setLatestEventInfo(ctx,
- ams.mContext.getString(
- com.android.internal.R.string
- .app_running_notification_title,
- appName),
- ams.mContext.getString(
- com.android.internal.R.string
- .app_running_notification_text,
- appName),
- pi);
- } catch (PackageManager.NameNotFoundException e) {
- foregroundNoti.icon = 0;
- }
+
+ // Do not allow apps to present a sneaky invisible content view either.
+ foregroundNoti.contentView = null;
+ foregroundNoti.bigContentView = null;
+ CharSequence appName = appInfo.loadLabel(
+ ams.mContext.getPackageManager());
+ if (appName == null) {
+ appName = appInfo.packageName;
+ }
+ Context ctx = null;
+ try {
+ ctx = ams.mContext.createPackageContext(
+ appInfo.packageName, 0);
+ Intent runningIntent = new Intent(
+ Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ runningIntent.setData(Uri.fromParts("package",
+ appInfo.packageName, null));
+ PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
+ runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ foregroundNoti.setLatestEventInfo(ctx,
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_title,
+ appName),
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_text,
+ appName),
+ pi);
+ } catch (PackageManager.NameNotFoundException e) {
+ foregroundNoti.icon = 0;
}
}
if (foregroundNoti.icon == 0) {
diff --git a/services/java/com/android/server/firewall/AndFilter.java b/services/java/com/android/server/firewall/AndFilter.java
new file mode 100644
index 0000000..e4276d0
--- /dev/null
+++ b/services/java/com/android/server/firewall/AndFilter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class AndFilter extends FilterList {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ for (int i=0; i<children.size(); i++) {
+ if (!children.get(i).matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType,
+ resolvedApp)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("and") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ return new AndFilter().readFromXml(parser);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/CategoryFilter.java b/services/java/com/android/server/firewall/CategoryFilter.java
new file mode 100644
index 0000000..4938cb8
--- /dev/null
+++ b/services/java/com/android/server/firewall/CategoryFilter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+class CategoryFilter implements Filter {
+ private static final String ATTR_NAME = "name";
+
+ private final String mCategoryName;
+
+ private CategoryFilter(String categoryName) {
+ mCategoryName = categoryName;
+ }
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ Set<String> categories = intent.getCategories();
+ if (categories == null) {
+ return false;
+ }
+ return categories.contains(mCategoryName);
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("category") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String categoryName = parser.getAttributeValue(null, ATTR_NAME);
+ if (categoryName == null) {
+ throw new XmlPullParserException("Category name must be specified.",
+ parser, null);
+ }
+ return new CategoryFilter(categoryName);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/Filter.java b/services/java/com/android/server/firewall/Filter.java
new file mode 100644
index 0000000..0e783e8
--- /dev/null
+++ b/services/java/com/android/server/firewall/Filter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+
+interface Filter {
+ /**
+ * Does the given intent + context info match this filter?
+ *
+ * @param ifw The IntentFirewall instance
+ * @param intent The intent being started/bound/broadcast
+ * @param callerApp An ApplicationInfo of an application in the caller's process. This may not
+ * be the specific app that is actually sending the intent. This also may be
+ * null, if the caller is the system process, or an unrecognized process (e.g.
+ * am start)
+ * @param callerUid
+ * @param callerPid
+ * @param resolvedType The resolved mime type of the intent
+ * @param resolvedApp The application that contains the resolved component that the intent is
+ */
+ boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp);
+}
diff --git a/services/java/com/android/server/firewall/FilterFactory.java b/services/java/com/android/server/firewall/FilterFactory.java
new file mode 100644
index 0000000..dea8b40
--- /dev/null
+++ b/services/java/com/android/server/firewall/FilterFactory.java
@@ -0,0 +1,40 @@
+/*
+ * 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.firewall;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+public abstract class FilterFactory {
+ private final String mTag;
+
+ protected FilterFactory(String tag) {
+ if (tag == null) {
+ throw new NullPointerException();
+ }
+ mTag = tag;
+ }
+
+ public String getTagName() {
+ return mTag;
+ }
+
+ public abstract Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException;
+}
diff --git a/services/java/com/android/server/firewall/FilterList.java b/services/java/com/android/server/firewall/FilterList.java
new file mode 100644
index 0000000..d34b203
--- /dev/null
+++ b/services/java/com/android/server/firewall/FilterList.java
@@ -0,0 +1,41 @@
+/*
+ * 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.firewall;
+
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+abstract class FilterList implements Filter {
+ protected final ArrayList<Filter> children = new ArrayList<Filter>();
+
+ public FilterList readFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ readChild(parser);
+ }
+ return this;
+ }
+
+ protected void readChild(XmlPullParser parser) throws IOException, XmlPullParserException {
+ Filter filter = IntentFirewall.parseFilter(parser);
+ children.add(filter);
+ }
+}
diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java
new file mode 100644
index 0000000..08e6b45
--- /dev/null
+++ b/services/java/com/android/server/firewall/IntentFirewall.java
@@ -0,0 +1,401 @@
+/*
+ * 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.firewall;
+
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import com.android.server.EventLogTags;
+import com.android.server.IntentResolver;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class IntentFirewall {
+ private static final String TAG = "IntentFirewall";
+
+ // e.g. /data/system/ifw/ifw.xml or /data/secure/system/ifw/ifw.xml
+ private static final File RULES_FILE =
+ new File(Environment.getSystemSecureDirectory(), "ifw/ifw.xml");
+
+ private static final int LOG_PACKAGES_MAX_LENGTH = 150;
+ private static final int LOG_PACKAGES_SUFFICIENT_LENGTH = 125;
+
+ private static final String TAG_RULES = "rules";
+ private static final String TAG_ACTIVITY = "activity";
+ private static final String TAG_SERVICE = "service";
+ private static final String TAG_BROADCAST = "broadcast";
+
+ private static final int TYPE_ACTIVITY = 0;
+ private static final int TYPE_SERVICE = 1;
+ private static final int TYPE_BROADCAST = 2;
+
+ private static final HashMap<String, FilterFactory> factoryMap;
+
+ private final AMSInterface mAms;
+
+ private final IntentResolver<FirewallIntentFilter, Rule> mActivityResolver =
+ new FirewallIntentResolver();
+ private final IntentResolver<FirewallIntentFilter, Rule> mServiceResolver =
+ new FirewallIntentResolver();
+ private final IntentResolver<FirewallIntentFilter, Rule> mBroadcastResolver =
+ new FirewallIntentResolver();
+
+ static {
+ FilterFactory[] factories = new FilterFactory[] {
+ AndFilter.FACTORY,
+ OrFilter.FACTORY,
+ NotFilter.FACTORY,
+
+ StringFilter.ACTION,
+ StringFilter.COMPONENT,
+ StringFilter.COMPONENT_NAME,
+ StringFilter.COMPONENT_PACKAGE,
+ StringFilter.DATA,
+ StringFilter.HOST,
+ StringFilter.MIME_TYPE,
+ StringFilter.PATH,
+ StringFilter.SSP,
+
+ CategoryFilter.FACTORY,
+ SenderFilter.FACTORY,
+ SenderPermissionFilter.FACTORY,
+ PortFilter.FACTORY
+ };
+
+ // load factor ~= .75
+ factoryMap = new HashMap<String, FilterFactory>(factories.length * 4 / 3);
+ for (int i=0; i<factories.length; i++) {
+ FilterFactory factory = factories[i];
+ factoryMap.put(factory.getTagName(), factory);
+ }
+ }
+
+ public IntentFirewall(AMSInterface ams) {
+ mAms = ams;
+ readRules(getRulesFile());
+ }
+
+ public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp, int callerUid,
+ int callerPid, String resolvedType, ActivityInfo resolvedActivity) {
+ List<Rule> matchingRules = mActivityResolver.queryIntent(intent, resolvedType, false, 0);
+ boolean log = false;
+ boolean block = false;
+
+ for (int i=0; i< matchingRules.size(); i++) {
+ Rule rule = matchingRules.get(i);
+ if (rule.matches(this, intent, callerApp, callerUid, callerPid, resolvedType,
+ resolvedActivity.applicationInfo)) {
+ block |= rule.getBlock();
+ log |= rule.getLog();
+
+ // if we've already determined that we should both block and log, there's no need
+ // to continue trying rules
+ if (block && log) {
+ break;
+ }
+ }
+ }
+
+ if (log) {
+ logIntent(TYPE_ACTIVITY, intent, callerUid, resolvedType);
+ }
+
+ return !block;
+ }
+
+ private static void logIntent(int intentType, Intent intent, int callerUid,
+ String resolvedType) {
+ // The component shouldn't be null, but let's double check just to be safe
+ ComponentName cn = intent.getComponent();
+ String shortComponent = null;
+ if (cn != null) {
+ shortComponent = cn.flattenToShortString();
+ }
+
+ String callerPackages = null;
+ int callerPackageCount = 0;
+ IPackageManager pm = AppGlobals.getPackageManager();
+ if (pm != null) {
+ try {
+ String[] callerPackagesArray = pm.getPackagesForUid(callerUid);
+ if (callerPackagesArray != null) {
+ callerPackageCount = callerPackagesArray.length;
+ callerPackages = joinPackages(callerPackagesArray);
+ }
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Remote exception while retrieving packages", ex);
+ }
+ }
+
+ EventLogTags.writeIfwIntentMatched(intentType, shortComponent, callerUid,
+ callerPackageCount, callerPackages, intent.getAction(), resolvedType,
+ intent.getDataString(), intent.getFlags());
+ }
+
+ /**
+ * Joins a list of package names such that the resulting string is no more than
+ * LOG_PACKAGES_MAX_LENGTH.
+ *
+ * Only full package names will be added to the result, unless every package is longer than the
+ * limit, in which case one of the packages will be truncated and added. In this case, an
+ * additional '-' character will be added to the end of the string, to denote the truncation.
+ *
+ * If it encounters a package that won't fit in the remaining space, it will continue on to the
+ * next package, unless the total length of the built string so far is greater than
+ * LOG_PACKAGES_SUFFICIENT_LENGTH, in which case it will stop and return what it has.
+ */
+ private static String joinPackages(String[] packages) {
+ boolean first = true;
+ StringBuilder sb = new StringBuilder();
+ for (int i=0; i<packages.length; i++) {
+ String pkg = packages[i];
+
+ // + 1 length for the comma. This logic technically isn't correct for the first entry,
+ // but it's not critical.
+ if (sb.length() + pkg.length() + 1 < LOG_PACKAGES_MAX_LENGTH) {
+ if (!first) {
+ sb.append(',');
+ } else {
+ first = false;
+ }
+ sb.append(pkg);
+ } else if (sb.length() >= LOG_PACKAGES_SUFFICIENT_LENGTH) {
+ return sb.toString();
+ }
+ }
+ if (sb.length() == 0 && packages.length > 0) {
+ String pkg = packages[0];
+ // truncating from the end - the last part of the package name is more likely to be
+ // interesting/unique
+ return pkg.substring(pkg.length() - LOG_PACKAGES_MAX_LENGTH + 1) + '-';
+ }
+ return null;
+ }
+
+ public static File getRulesFile() {
+ return RULES_FILE;
+ }
+
+ private void readRules(File rulesFile) {
+ FileInputStream fis;
+ try {
+ fis = new FileInputStream(rulesFile);
+ } catch (FileNotFoundException ex) {
+ // Nope, no rules. Nothing else to do!
+ return;
+ }
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+
+ parser.setInput(fis, null);
+
+ XmlUtils.beginDocument(parser, TAG_RULES);
+
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ IntentResolver<FirewallIntentFilter, Rule> resolver = null;
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_ACTIVITY)) {
+ resolver = mActivityResolver;
+ } else if (tagName.equals(TAG_SERVICE)) {
+ resolver = mServiceResolver;
+ } else if (tagName.equals(TAG_BROADCAST)) {
+ resolver = mBroadcastResolver;
+ }
+
+ if (resolver != null) {
+ Rule rule = new Rule();
+
+ try {
+ rule.readFromXml(parser);
+ } catch (XmlPullParserException ex) {
+ Slog.e(TAG, "Error reading intent firewall rule", ex);
+ continue;
+ } catch (IOException ex) {
+ Slog.e(TAG, "Error reading intent firewall rule", ex);
+ continue;
+ }
+
+ for (int i=0; i<rule.getIntentFilterCount(); i++) {
+ resolver.addFilter(rule.getIntentFilter(i));
+ }
+ }
+ }
+ } catch (XmlPullParserException ex) {
+ Slog.e(TAG, "Error reading intent firewall rules", ex);
+ } catch (IOException ex) {
+ Slog.e(TAG, "Error reading intent firewall rules", ex);
+ } finally {
+ try {
+ fis.close();
+ } catch (IOException ex) {
+ Slog.e(TAG, "Error while closing " + rulesFile, ex);
+ }
+ }
+ }
+
+ static Filter parseFilter(XmlPullParser parser) throws IOException, XmlPullParserException {
+ String elementName = parser.getName();
+
+ FilterFactory factory = factoryMap.get(elementName);
+
+ if (factory == null) {
+ throw new XmlPullParserException("Unknown element in filter list: " + elementName);
+ }
+ return factory.newFilter(parser);
+ }
+
+ private static class Rule extends AndFilter {
+ private static final String TAG_INTENT_FILTER = "intent-filter";
+
+ private static final String ATTR_BLOCK = "block";
+ private static final String ATTR_LOG = "log";
+
+ private final ArrayList<FirewallIntentFilter> mIntentFilters =
+ new ArrayList<FirewallIntentFilter>(1);
+ private boolean block;
+ private boolean log;
+
+ @Override
+ public Rule readFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+ block = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_BLOCK));
+ log = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_LOG));
+
+ super.readFromXml(parser);
+ return this;
+ }
+
+ @Override
+ protected void readChild(XmlPullParser parser) throws IOException, XmlPullParserException {
+ if (parser.getName().equals(TAG_INTENT_FILTER)) {
+ FirewallIntentFilter intentFilter = new FirewallIntentFilter(this);
+ intentFilter.readFromXml(parser);
+ mIntentFilters.add(intentFilter);
+ } else {
+ super.readChild(parser);
+ }
+ }
+
+ public int getIntentFilterCount() {
+ return mIntentFilters.size();
+ }
+
+ public FirewallIntentFilter getIntentFilter(int index) {
+ return mIntentFilters.get(index);
+ }
+
+ public boolean getBlock() {
+ return block;
+ }
+
+ public boolean getLog() {
+ return log;
+ }
+ }
+
+ private static class FirewallIntentFilter extends IntentFilter {
+ private final Rule rule;
+
+ public FirewallIntentFilter(Rule rule) {
+ this.rule = rule;
+ }
+ }
+
+ private static class FirewallIntentResolver
+ extends IntentResolver<FirewallIntentFilter, Rule> {
+ @Override
+ protected boolean allowFilterResult(FirewallIntentFilter filter, List<Rule> dest) {
+ return !dest.contains(filter.rule);
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName, FirewallIntentFilter filter) {
+ return true;
+ }
+
+ @Override
+ protected FirewallIntentFilter[] newArray(int size) {
+ return new FirewallIntentFilter[size];
+ }
+
+ @Override
+ protected Rule newResult(FirewallIntentFilter filter, int match, int userId) {
+ return filter.rule;
+ }
+
+ @Override
+ protected void sortResults(List<Rule> results) {
+ // there's no need to sort the results
+ return;
+ }
+ }
+
+ /**
+ * This interface contains the methods we need from ActivityManagerService. This allows AMS to
+ * export these methods to us without making them public, and also makes it easier to test this
+ * component.
+ */
+ public interface AMSInterface {
+ int checkComponentPermission(String permission, int pid, int uid,
+ int owningUid, boolean exported);
+ }
+
+ /**
+ * Checks if the caller has access to a component
+ *
+ * @param permission If present, the caller must have this permission
+ * @param pid The pid of the caller
+ * @param uid The uid of the caller
+ * @param owningUid The uid of the application that owns the component
+ * @param exported Whether the component is exported
+ * @return True if the caller can access the described component
+ */
+ boolean checkComponentPermission(String permission, int pid, int uid, int owningUid,
+ boolean exported) {
+ return mAms.checkComponentPermission(permission, pid, uid, owningUid, exported) ==
+ PackageManager.PERMISSION_GRANTED;
+ }
+
+ boolean signaturesMatch(int uid1, int uid2) {
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Remote exception while checking signatures", ex);
+ return false;
+ }
+ }
+}
diff --git a/services/java/com/android/server/firewall/NotFilter.java b/services/java/com/android/server/firewall/NotFilter.java
new file mode 100644
index 0000000..f0fc337
--- /dev/null
+++ b/services/java/com/android/server/firewall/NotFilter.java
@@ -0,0 +1,59 @@
+/*
+ * 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.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class NotFilter implements Filter {
+ private final Filter mChild;
+
+ private NotFilter(Filter child) {
+ mChild = child;
+ }
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ return !mChild.matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType,
+ resolvedApp);
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("not") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ Filter child = null;
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ Filter filter = IntentFirewall.parseFilter(parser);
+ if (child == null) {
+ child = filter;
+ } else {
+ throw new XmlPullParserException(
+ "<not> tag can only contain a single child filter.", parser, null);
+ }
+ }
+ return new NotFilter(child);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/OrFilter.java b/services/java/com/android/server/firewall/OrFilter.java
new file mode 100644
index 0000000..72db31e
--- /dev/null
+++ b/services/java/com/android/server/firewall/OrFilter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class OrFilter extends FilterList {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ for (int i=0; i<children.size(); i++) {
+ if (children.get(i).matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType,
+ resolvedApp)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("or") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ return new OrFilter().readFromXml(parser);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/PortFilter.java b/services/java/com/android/server/firewall/PortFilter.java
new file mode 100644
index 0000000..fe7e085
--- /dev/null
+++ b/services/java/com/android/server/firewall/PortFilter.java
@@ -0,0 +1,110 @@
+/*
+ * 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.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class PortFilter implements Filter {
+ private static final String ATTR_EQUALS = "equals";
+ private static final String ATTR_MIN = "min";
+ private static final String ATTR_MAX = "max";
+
+ private static final int NO_BOUND = -1;
+
+ // both bounds are inclusive
+ private final int mLowerBound;
+ private final int mUpperBound;
+
+ private PortFilter(int lowerBound, int upperBound) {
+ mLowerBound = lowerBound;
+ mUpperBound = upperBound;
+ }
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ int port = -1;
+ Uri uri = intent.getData();
+ if (uri != null) {
+ port = uri.getPort();
+ }
+ return port != -1 &&
+ (mLowerBound == NO_BOUND || mLowerBound <= port) &&
+ (mUpperBound == NO_BOUND || mUpperBound >= port);
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("port") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int lowerBound = NO_BOUND;
+ int upperBound = NO_BOUND;
+
+ String equalsValue = parser.getAttributeValue(null, ATTR_EQUALS);
+ if (equalsValue != null) {
+ int value;
+ try {
+ value = Integer.parseInt(equalsValue);
+ } catch (NumberFormatException ex) {
+ throw new XmlPullParserException("Invalid port value: " + equalsValue,
+ parser, null);
+ }
+ lowerBound = value;
+ upperBound = value;
+ }
+
+ String lowerBoundString = parser.getAttributeValue(null, ATTR_MIN);
+ String upperBoundString = parser.getAttributeValue(null, ATTR_MAX);
+ if (lowerBoundString != null || upperBoundString != null) {
+ if (equalsValue != null) {
+ throw new XmlPullParserException(
+ "Port filter cannot use both equals and range filtering",
+ parser, null);
+ }
+
+ if (lowerBoundString != null) {
+ try {
+ lowerBound = Integer.parseInt(lowerBoundString);
+ } catch (NumberFormatException ex) {
+ throw new XmlPullParserException(
+ "Invalid minimum port value: " + lowerBoundString,
+ parser, null);
+ }
+ }
+
+ if (upperBoundString != null) {
+ try {
+ upperBound = Integer.parseInt(upperBoundString);
+ } catch (NumberFormatException ex) {
+ throw new XmlPullParserException(
+ "Invalid maximum port value: " + upperBoundString,
+ parser, null);
+ }
+ }
+ }
+
+ // an empty port filter is explicitly allowed, and checks for the existence of a port
+ return new PortFilter(lowerBound, upperBound);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/SenderFilter.java b/services/java/com/android/server/firewall/SenderFilter.java
new file mode 100644
index 0000000..58bdd73
--- /dev/null
+++ b/services/java/com/android/server/firewall/SenderFilter.java
@@ -0,0 +1,111 @@
+/*
+ * 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.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class SenderFilter {
+ private static final String ATTR_TYPE = "type";
+
+ private static final String VAL_SIGNATURE = "signature";
+ private static final String VAL_SYSTEM = "system";
+ private static final String VAL_SYSTEM_OR_SIGNATURE = "system|signature";
+ private static final String VAL_USER_ID = "userId";
+
+ static boolean isSystemApp(ApplicationInfo callerApp, int callerUid, int callerPid) {
+ if (callerUid == Process.SYSTEM_UID ||
+ callerPid == Process.myPid()) {
+ return true;
+ }
+ if (callerApp == null) {
+ return false;
+ }
+ return (callerApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("sender") {
+ @Override
+ public Filter newFilter(XmlPullParser parser) throws IOException, XmlPullParserException {
+ String typeString = parser.getAttributeValue(null, ATTR_TYPE);
+ if (typeString == null) {
+ throw new XmlPullParserException("type attribute must be specified for <sender>",
+ parser, null);
+ }
+ if (typeString.equals(VAL_SYSTEM)) {
+ return SYSTEM;
+ } else if (typeString.equals(VAL_SIGNATURE)) {
+ return SIGNATURE;
+ } else if (typeString.equals(VAL_SYSTEM_OR_SIGNATURE)) {
+ return SYSTEM_OR_SIGNATURE;
+ } else if (typeString.equals(VAL_USER_ID)) {
+ return USER_ID;
+ }
+ throw new XmlPullParserException(
+ "Invalid type attribute for <sender>: " + typeString, parser, null);
+ }
+ };
+
+ private static final Filter SIGNATURE = new Filter() {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ if (callerApp == null) {
+ return false;
+ }
+ return ifw.signaturesMatch(callerUid, resolvedApp.uid);
+ }
+ };
+
+ private static final Filter SYSTEM = new Filter() {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ if (callerApp == null) {
+ // if callerApp is null, the caller is the system process
+ return false;
+ }
+ return isSystemApp(callerApp, callerUid, callerPid);
+ }
+ };
+
+ private static final Filter SYSTEM_OR_SIGNATURE = new Filter() {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ return isSystemApp(callerApp, callerUid, callerPid) ||
+ ifw.signaturesMatch(callerUid, resolvedApp.uid);
+ }
+ };
+
+ private static final Filter USER_ID = new Filter() {
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ // This checks whether the caller is either the system process, or has the same user id
+ // I.e. the same app, or an app that uses the same shared user id.
+ // This is the same set of applications that would be able to access the component if
+ // it wasn't exported.
+ return ifw.checkComponentPermission(null, callerPid, callerUid, resolvedApp.uid, false);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/SenderPermissionFilter.java b/services/java/com/android/server/firewall/SenderPermissionFilter.java
new file mode 100644
index 0000000..310da20
--- /dev/null
+++ b/services/java/com/android/server/firewall/SenderPermissionFilter.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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class SenderPermissionFilter implements Filter {
+ private static final String ATTR_NAME = "name";
+
+ private final String mPermission;
+
+ private SenderPermissionFilter(String permission) {
+ mPermission = permission;
+ }
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ // We assume the component is exported here. If the component is not exported, then
+ // ActivityManager would only resolve to this component for callers from the same uid.
+ // In this case, it doesn't matter whether the component is exported or not.
+ return ifw.checkComponentPermission(mPermission, callerPid, callerUid, resolvedApp.uid,
+ true);
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("sender-permission") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String permission = parser.getAttributeValue(null, ATTR_NAME);
+ if (permission == null) {
+ throw new XmlPullParserException("Permission name must be specified.",
+ parser, null);
+ }
+ return new SenderPermissionFilter(permission);
+ }
+ };
+}
diff --git a/services/java/com/android/server/firewall/StringFilter.java b/services/java/com/android/server/firewall/StringFilter.java
new file mode 100644
index 0000000..ed5d3f3
--- /dev/null
+++ b/services/java/com/android/server/firewall/StringFilter.java
@@ -0,0 +1,342 @@
+/*
+ * 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.firewall;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import android.os.PatternMatcher;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+abstract class StringFilter implements Filter {
+ private static final String ATTR_EQUALS = "equals";
+ private static final String ATTR_STARTS_WITH = "startsWith";
+ private static final String ATTR_CONTAINS = "contains";
+ private static final String ATTR_PATTERN = "pattern";
+ private static final String ATTR_REGEX = "regex";
+ private static final String ATTR_IS_NULL = "isNull";
+
+ private final ValueProvider mValueProvider;
+
+ private StringFilter(ValueProvider valueProvider) {
+ this.mValueProvider = valueProvider;
+ }
+
+ /**
+ * Constructs a new StringFilter based on the string filter attribute on the current
+ * element, and the given StringValueMatcher.
+ *
+ * The current node should contain exactly 1 string filter attribute. E.g. equals,
+ * contains, etc. Otherwise, an XmlPullParserException will be thrown.
+ *
+ * @param parser An XmlPullParser object positioned at an element that should
+ * contain a string filter attribute
+ * @return This StringFilter object
+ */
+ public static StringFilter readFromXml(ValueProvider valueProvider, XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ StringFilter filter = null;
+
+ for (int i=0; i<parser.getAttributeCount(); i++) {
+ StringFilter newFilter = getFilter(valueProvider, parser, i);
+ if (newFilter != null) {
+ if (filter != null) {
+ throw new XmlPullParserException("Multiple string filter attributes found");
+ }
+ filter = newFilter;
+ }
+ }
+
+ if (filter == null) {
+ // if there are no string filter attributes, we default to isNull="false" so that an
+ // empty filter is equivalent to an existence check
+ filter = new IsNullFilter(valueProvider, false);
+ }
+
+ return filter;
+ }
+
+ private static StringFilter getFilter(ValueProvider valueProvider, XmlPullParser parser,
+ int attributeIndex) {
+ String attributeName = parser.getAttributeName(attributeIndex);
+
+ switch (attributeName.charAt(0)) {
+ case 'e':
+ if (!attributeName.equals(ATTR_EQUALS)) {
+ return null;
+ }
+ return new EqualsFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+ case 'i':
+ if (!attributeName.equals(ATTR_IS_NULL)) {
+ return null;
+ }
+ return new IsNullFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+ case 's':
+ if (!attributeName.equals(ATTR_STARTS_WITH)) {
+ return null;
+ }
+ return new StartsWithFilter(valueProvider,
+ parser.getAttributeValue(attributeIndex));
+ case 'c':
+ if (!attributeName.equals(ATTR_CONTAINS)) {
+ return null;
+ }
+ return new ContainsFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+ case 'p':
+ if (!attributeName.equals(ATTR_PATTERN)) {
+ return null;
+ }
+ return new PatternStringFilter(valueProvider,
+ parser.getAttributeValue(attributeIndex));
+ case 'r':
+ if (!attributeName.equals(ATTR_REGEX)) {
+ return null;
+ }
+ return new RegexFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+ }
+ return null;
+ }
+
+ protected abstract boolean matchesValue(String value);
+
+ @Override
+ public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+ int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+ String value = mValueProvider.getValue(intent, callerApp, resolvedType, resolvedApp);
+ return matchesValue(value);
+ }
+
+ private static abstract class ValueProvider extends FilterFactory {
+ protected ValueProvider(String tag) {
+ super(tag);
+ }
+
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ return StringFilter.readFromXml(this, parser);
+ }
+
+ public abstract String getValue(Intent intent, ApplicationInfo callerApp,
+ String resolvedType, ApplicationInfo resolvedApp);
+ }
+
+ private static class EqualsFilter extends StringFilter {
+ private final String mFilterValue;
+
+ public EqualsFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mFilterValue = attrValue;
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && value.equals(mFilterValue);
+ }
+ }
+
+ private static class ContainsFilter extends StringFilter {
+ private final String mFilterValue;
+
+ public ContainsFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mFilterValue = attrValue;
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && value.contains(mFilterValue);
+ }
+ }
+
+ private static class StartsWithFilter extends StringFilter {
+ private final String mFilterValue;
+
+ public StartsWithFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mFilterValue = attrValue;
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && value.startsWith(mFilterValue);
+ }
+ }
+
+ private static class PatternStringFilter extends StringFilter {
+ private final PatternMatcher mPattern;
+
+ public PatternStringFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mPattern = new PatternMatcher(attrValue, PatternMatcher.PATTERN_SIMPLE_GLOB);
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && mPattern.match(value);
+ }
+ }
+
+ private static class RegexFilter extends StringFilter {
+ private final Pattern mPattern;
+
+ public RegexFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ this.mPattern = Pattern.compile(attrValue);
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return value != null && mPattern.matcher(value).matches();
+ }
+ }
+
+ private static class IsNullFilter extends StringFilter {
+ private final boolean mIsNull;
+
+ public IsNullFilter(ValueProvider valueProvider, String attrValue) {
+ super(valueProvider);
+ mIsNull = Boolean.parseBoolean(attrValue);
+ }
+
+ public IsNullFilter(ValueProvider valueProvider, boolean isNull) {
+ super(valueProvider);
+ mIsNull = isNull;
+ }
+
+ @Override
+ public boolean matchesValue(String value) {
+ return (value == null) == mIsNull;
+ }
+ }
+
+ public static final ValueProvider COMPONENT = new ValueProvider("component") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ ComponentName cn = intent.getComponent();
+ if (cn != null) {
+ return cn.flattenToString();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider COMPONENT_NAME = new ValueProvider("component-name") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ ComponentName cn = intent.getComponent();
+ if (cn != null) {
+ return cn.getClassName();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider COMPONENT_PACKAGE = new ValueProvider("component-package") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ ComponentName cn = intent.getComponent();
+ if (cn != null) {
+ return cn.getPackageName();
+ }
+ return null;
+ }
+ };
+
+ public static final FilterFactory ACTION = new ValueProvider("action") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ return intent.getAction();
+ }
+ };
+
+ public static final ValueProvider DATA = new ValueProvider("data") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.toString();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider MIME_TYPE = new ValueProvider("mime-type") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ return resolvedType;
+ }
+ };
+
+ public static final ValueProvider SCHEME = new ValueProvider("scheme") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.getScheme();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider SSP = new ValueProvider("scheme-specific-part") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.getSchemeSpecificPart();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider HOST = new ValueProvider("host") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.getHost();
+ }
+ return null;
+ }
+ };
+
+ public static final ValueProvider PATH = new ValueProvider("path") {
+ @Override
+ public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+ ApplicationInfo resolvedApp) {
+ Uri data = intent.getData();
+ if (data != null) {
+ return data.getPath();
+ }
+ return null;
+ }
+ };
+}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index dc5916b..3d7dd63 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.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 @@
? (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 @@
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;
}
- if (DEBUG_INSTALL) {
- if (gp != ps) {
- Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
+ } else {
+ allowed = false;
+ }
+ 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");
+ 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 {
- 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)
- + ")");
- }
+ 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 @@
}
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,
@@ -5383,8 +5410,9 @@
}
@Override
- protected String packageForFilter(PackageParser.ActivityIntentInfo info) {
- return info.activity.owner.packageName;
+ protected boolean isPackageForFilter(String packageName,
+ PackageParser.ActivityIntentInfo info) {
+ return packageName.equals(info.activity.owner.packageName);
}
@Override
@@ -5580,8 +5608,9 @@
}
@Override
- protected String packageForFilter(PackageParser.ServiceIntentInfo info) {
- return info.service.owner.packageName;
+ protected boolean isPackageForFilter(String packageName,
+ PackageParser.ServiceIntentInfo info) {
+ return packageName.equals(info.service.owner.packageName);
}
@Override
@@ -8359,6 +8388,10 @@
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;
}
@@ -8614,6 +8647,19 @@
mSettings.writeLPr();
}
}
+ if (outInfo != null) {
+ // 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));
+ }
+ }
+ }
+ }
}
/*
diff --git a/services/java/com/android/server/pm/PreferredIntentResolver.java b/services/java/com/android/server/pm/PreferredIntentResolver.java
index 3f1e50c..7fe6a05 100644
--- a/services/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/java/com/android/server/pm/PreferredIntentResolver.java
@@ -27,8 +27,8 @@
return new PreferredActivity[size];
}
@Override
- protected String packageForFilter(PreferredActivity filter) {
- return filter.mPref.mComponent.getPackageName();
+ protected boolean isPackageForFilter(String packageName, PreferredActivity filter) {
+ return packageName.equals(filter.mPref.mComponent.getPackageName());
}
@Override
protected void dumpFilter(PrintWriter out, String prefix,
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 70183df..2e48074 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1603,9 +1603,6 @@
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;
}
diff --git a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
index d603cfa..c94f7c1 100644
--- a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
+++ b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
@@ -226,8 +226,6 @@
FileOutputStream out = null;
File tmp = null;
try {
- // create the temporary file
- tmp = File.createTempFile("journal", "", dir);
// create the parents for the destination file
File parent = file.getParentFile();
parent.mkdirs();
@@ -235,6 +233,8 @@
if (!parent.exists()) {
throw new IOException("Failed to create directory " + parent.getCanonicalPath());
}
+ // create the temporary file
+ tmp = File.createTempFile("journal", "", dir);
// mark tmp -rw-r--r--
tmp.setReadable(true, false);
// write to it
diff --git a/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java
new file mode 100644
index 0000000..9185903
--- /dev/null
+++ b/services/java/com/android/server/updates/IntentFirewallInstallReceiver.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.android.server.firewall.IntentFirewall;
+
+public class IntentFirewallInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ public IntentFirewallInstallReceiver() {
+ super(IntentFirewall.getRulesFile().getParent(), IntentFirewall.getRulesFile().getName(),
+ "metadata/", "version");
+ }
+}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 34052f3..af603fd 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5365,6 +5365,9 @@
if (maxLayer < winAnim.mSurfaceLayer) {
maxLayer = winAnim.mSurfaceLayer;
}
+ if (minLayer > winAnim.mSurfaceLayer) {
+ minLayer = winAnim.mSurfaceLayer;
+ }
// Don't include wallpaper in bounds calculation
if (!ws.mIsWallpaper) {
@@ -5377,17 +5380,14 @@
frame.union(left, top, right, bottom);
}
- if (ws.mAppToken != null && ws.mAppToken.token == appToken) {
- if (minLayer > ws.mWinAnimator.mSurfaceLayer) {
- minLayer = ws.mWinAnimator.mSurfaceLayer;
- }
- if (ws.isDisplayedLw()) {
- screenshotReady = true;
- }
- if (fullscreen) {
- // No point in continuing down through windows.
- break;
- }
+ if (ws.mAppToken != null && ws.mAppToken.token == appToken &&
+ ws.isDisplayedLw()) {
+ screenshotReady = true;
+ }
+
+ if (fullscreen) {
+ // No point in continuing down through windows.
+ break;
}
}
@@ -5461,14 +5461,12 @@
rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer);
}
} while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
- if (DEBUG_SCREENSHOT && retryCount > MAX_SCREENSHOT_RETRIES) {
- Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken + " appWin="
- + (appWin == null ? "null" : (appWin + " drawState="
- + appWin.mWinAnimator.mDrawState)));
- }
+ if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " +
+ retryCount + " of " + appToken + " appWin=" + (appWin == null ?
+ "null" : (appWin + " drawState=" + appWin.mWinAnimator.mDrawState)));
if (rawss == null) {
- Slog.w(TAG, "Failure taking screenshot for (" + dw + "x" + dh
+ Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh
+ ") to layer " + maxLayer);
return null;
}
@@ -5481,7 +5479,7 @@
canvas.drawBitmap(rawss, matrix, null);
canvas.setBitmap(null);
- if (DEBUG_SCREENSHOT) {
+ if (true || DEBUG_SCREENSHOT) {
// TEST IF IT's ALL BLACK
int[] buffer = new int[bm.getWidth() * bm.getHeight()];
bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
@@ -5494,7 +5492,8 @@
}
if (allBlack) {
Slog.i(TAG, "Screenshot " + appWin + " was all black! mSurfaceLayer=" +
- (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null"));
+ (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null") +
+ " minLayer=" + minLayer + " maxLayer=" + maxLayer);
}
}
@@ -5803,6 +5802,19 @@
}
}
+ @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/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 3ed9cef..674955c 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -274,6 +274,33 @@
}
/**
+ * Make a SignalStrength object from the given parcel as passed up by
+ * the ril which does not have isGsm. isGsm will be changed by ServiceStateTracker
+ * so the default is a don't care.
+ *
+ * @hide
+ */
+ public static SignalStrength makeSignalStrengthFromRilParcel(Parcel in) {
+ if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
+
+ SignalStrength ss = new SignalStrength();
+ ss.mGsmSignalStrength = in.readInt();
+ ss.mGsmBitErrorRate = in.readInt();
+ ss.mCdmaDbm = in.readInt();
+ ss.mCdmaEcio = in.readInt();
+ ss.mEvdoDbm = in.readInt();
+ ss.mEvdoEcio = in.readInt();
+ ss.mEvdoSnr = in.readInt();
+ ss.mLteSignalStrength = in.readInt();
+ ss.mLteRsrp = in.readInt();
+ ss.mLteRsrq = in.readInt();
+ ss.mLteRssnr = in.readInt();
+ ss.mLteCqi = in.readInt();
+
+ return ss;
+ }
+
+ /**
* {@link Parcelable#writeToParcel}
*/
public void writeToParcel(Parcel out, int flags) {
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 @@
}
/**
+ * 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/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 @@
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/tests/RenderScriptTests/FBOTest/Android.mk b/tests/RenderScriptTests/FBOTest/Android.mk
index 434d592..7a578d9 100644
--- a/tests/RenderScriptTests/FBOTest/Android.mk
+++ b/tests/RenderScriptTests/FBOTest/Android.mk
@@ -23,4 +23,6 @@
LOCAL_PACKAGE_NAME := FBOTest
+LOCAL_SDK_VERSION := 17
+
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/Fountain/Android.mk b/tests/RenderScriptTests/Fountain/Android.mk
index 4a6560b..0517aef 100644
--- a/tests/RenderScriptTests/Fountain/Android.mk
+++ b/tests/RenderScriptTests/Fountain/Android.mk
@@ -21,8 +21,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-# TODO: build fails with this set
-# LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 17
LOCAL_PACKAGE_NAME := RsFountain
diff --git a/tests/RenderScriptTests/HelloWorld/Android.mk b/tests/RenderScriptTests/HelloWorld/Android.mk
index 54824f4..c1c08ec 100644
--- a/tests/RenderScriptTests/HelloWorld/Android.mk
+++ b/tests/RenderScriptTests/HelloWorld/Android.mk
@@ -23,6 +23,6 @@
LOCAL_PACKAGE_NAME := RsHelloWorld
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 17
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/MiscSamples/Android.mk b/tests/RenderScriptTests/MiscSamples/Android.mk
index bdff46a..ee3567b 100644
--- a/tests/RenderScriptTests/MiscSamples/Android.mk
+++ b/tests/RenderScriptTests/MiscSamples/Android.mk
@@ -23,6 +23,6 @@
LOCAL_PACKAGE_NAME := RsMiscSamples
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 17
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ModelViewer/Android.mk b/tests/RenderScriptTests/ModelViewer/Android.mk
index 18ceac5..86724cf 100644
--- a/tests/RenderScriptTests/ModelViewer/Android.mk
+++ b/tests/RenderScriptTests/ModelViewer/Android.mk
@@ -22,6 +22,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+LOCAL_SDK_VERSION := 17
+
LOCAL_PACKAGE_NAME := ModelViewer
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/PerfTest/Android.mk b/tests/RenderScriptTests/PerfTest/Android.mk
index 0d1e7d2..e9ee771 100644
--- a/tests/RenderScriptTests/PerfTest/Android.mk
+++ b/tests/RenderScriptTests/PerfTest/Android.mk
@@ -24,6 +24,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+LOCAL_SDK_VERSION := 17
+
LOCAL_PACKAGE_NAME := PerfTest
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/SceneGraph/Android.mk b/tests/RenderScriptTests/SceneGraph/Android.mk
index 163a95d..6047305 100644
--- a/tests/RenderScriptTests/SceneGraph/Android.mk
+++ b/tests/RenderScriptTests/SceneGraph/Android.mk
@@ -21,6 +21,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SDK_VERSION := 17
+
LOCAL_PACKAGE_NAME := SceneGraphTest
include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ShadersTest/Android.mk b/tests/RenderScriptTests/ShadersTest/Android.mk
index 0912591..fb6356e 100644
--- a/tests/RenderScriptTests/ShadersTest/Android.mk
+++ b/tests/RenderScriptTests/ShadersTest/Android.mk
@@ -21,6 +21,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SDK_VERSION := 17
+
LOCAL_PACKAGE_NAME := ShadersTest
include $(BUILD_PACKAGE)
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
index 5d0b155..c7c8aa2 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
@@ -49,6 +49,8 @@
{
private final static String TAG = "NotificationTestList";
+ private final static String NOTIFY_TAG = "foo";
+
NotificationManager mNM;
Handler mHandler;
int mStartDelay;
@@ -196,7 +198,7 @@
final Notification n = buildNotification(id);
mHandler.postDelayed(new Runnable() {
public void run() {
- mNM.notify(id, n);
+ mNM.notify(NOTIFY_TAG, id, n);
}
}, mStartDelay);
}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 4345098..ba160b18 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -33,13 +33,10 @@
import android.net.Uri;
import android.os.SystemClock;
import android.widget.RemoteViews;
-import android.widget.TextView;
-import android.widget.ProgressBar;
import android.os.PowerManager;
// private NM API
import android.app.INotificationManager;
-import com.android.internal.statusbar.StatusBarNotification;
public class NotificationTestList extends TestActivity
{
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 84f5a5c..cadac02 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -592,6 +592,10 @@
goto bail;
}
printf("uses-permission: %s\n", name.string());
+ int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
+ if (!req) {
+ printf("optional-permission: %s\n", name.string());
+ }
}
}
} else if (strcmp("badging", option) == 0) {
@@ -1033,6 +1037,10 @@
hasWriteCallLogPermission = true;
}
printf("uses-permission:'%s'\n", name.string());
+ int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
+ if (!req) {
+ printf("optional-permission:'%s'\n", name.string());
+ }
} else {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
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 @@
}
@Override
+ public void removeRotationWatcher(IRotationWatcher arg0) throws RemoteException {
+ }
+
+ @Override
public boolean waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
return false;
}