Merge "Use the parameter type as part of the methods cache key"
diff --git a/Android.mk b/Android.mk
index d81e581..98bd88d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,7 +26,10 @@
# TODO: find a more appropriate way to do this.
framework_res_source_path := APPS/framework-res_intermediates/src
-# the library
+# Build the master framework library.
+# The framework contains too many method references (>64K) for poor old DEX.
+# So we first build the framework as a monolithic static library then split it
+# up into smaller pieces.
# ============================================================
include $(CLEAR_VARS)
@@ -39,14 +42,6 @@
core/java/android/speech/tts/EventLogTags.logtags \
core/java/android/webkit/EventLogTags.logtags \
-# The following filters out code we are temporarily not including at all.
-# TODO: Move AWT and beans (and associated harmony code) back into libcore.
-# TODO: Maybe remove javax.microedition entirely?
-# TODO: Move SyncML (org.mobilecontrol.*) into its own library.
-LOCAL_SRC_FILES := $(filter-out \
- org/mobilecontrol/% \
- ,$(LOCAL_SRC_FILES))
-
## READ ME: ########################################################
##
## When updating this list of aidl files, consider if that aidl is
@@ -258,8 +253,6 @@
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
-#
-
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
@@ -272,17 +265,11 @@
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt core core-junit ext okhttp
-LOCAL_MODULE := framework
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE := framework-base
-# List of classes and interfaces which should be loaded by the Zygote.
-LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
+LOCAL_JAR_EXCLUDE_FILES := none
-#LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
-
-LOCAL_DX_FLAGS := --core-library
-
-include $(BUILD_JAVA_LIBRARY)
+include $(BUILD_STATIC_JAVA_LIBRARY)
# Make sure that R.java and Manifest.java are built before we build
# the source for this library.
@@ -290,14 +277,53 @@
$(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
$(full_classes_compiled_jar): $(framework_res_R_stamp)
-# Make sure that framework-res is installed when framework is.
-$(LOCAL_INSTALLED_MODULE): | $(dir $(LOCAL_INSTALLED_MODULE))framework-res.apk
-
-framework_built := $(call java-lib-deps,framework)
-
-# AIDL files to be preprocessed and included in the SDK,
-# relative to the root of the build tree.
+# Build part 1 of the framework library.
# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := framework
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_STATIC_JAVA_LIBRARIES := framework-base
+LOCAL_DX_FLAGS := --core-library
+
+# Packages to include, use \* wildcard to include descendants.
+LOCAL_JAR_PACKAGES := android\*
+
+# List of classes and interfaces which should be loaded by the Zygote.
+LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
+
+include $(BUILD_JAVA_LIBRARY)
+framework_module := $(LOCAL_INSTALLED_MODULE)
+
+# Build part 2 of the framework library.
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := framework2
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_STATIC_JAVA_LIBRARIES := framework-base
+LOCAL_DX_FLAGS := --core-library
+
+# Packages to include, use \* wildcard to include descendants.
+LOCAL_JAR_PACKAGES := com\* javax\*
+
+include $(BUILD_JAVA_LIBRARY)
+framework2_module := $(LOCAL_INSTALLED_MODULE)
+
+# Make sure that all framework modules are installed when framework is.
+# ============================================================
+$(framework_module): | $(dir $(framework_module))framework-res.apk
+$(framework_module): | $(dir $(framework_module))framework2.jar
+
+framework_built := $(call java-lib-deps,framework framework2)
+
+# Copy AIDL files to be preprocessed and included in the SDK,
+# specified relative to the root of the build tree.
+# ============================================================
+include $(CLEAR_VARS)
+
aidl_files := \
frameworks/base/core/java/android/accounts/IAccountManager.aidl \
frameworks/base/core/java/android/accounts/IAccountManagerResponse.aidl \
@@ -443,6 +469,7 @@
okhttp \
ext \
framework \
+ framework2 \
mms-common \
telephony-common \
voip-common
@@ -479,7 +506,7 @@
-overview $(LOCAL_PATH)/core/java/overview.html
framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
- $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
+ $(call intermediates-dir-for,JAVA_LIBRARIES,framework-base,,COMMON)
framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) \
@@ -761,7 +788,7 @@
LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) framework
+LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
diff --git a/api/current.txt b/api/current.txt
index 9073348..080c6f9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -259,6 +259,7 @@
field public static final int addPrintersActivity = 16843747; // 0x10103e3
field public static final int addStatesFromChildren = 16842992; // 0x10100f0
field public static final int adjustViewBounds = 16843038; // 0x101011e
+ field public static final int aid = 16843750; // 0x10103e6
field public static final int alertDialogIcon = 16843605; // 0x1010355
field public static final int alertDialogStyle = 16842845; // 0x101005d
field public static final int alertDialogTheme = 16843529; // 0x1010309
@@ -793,6 +794,7 @@
field public static final int path = 16842794; // 0x101002a
field public static final int pathPattern = 16842796; // 0x101002c
field public static final int pathPrefix = 16842795; // 0x101002b
+ field public static final int paymentService = 16843749; // 0x10103e5
field public static final int permission = 16842758; // 0x1010006
field public static final int permissionFlags = 16843719; // 0x10103c7
field public static final int permissionGroup = 16842762; // 0x101000a
@@ -3052,17 +3054,19 @@
public class AlarmManager {
method public void cancel(android.app.PendingIntent);
method public void set(int, long, android.app.PendingIntent);
- method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
+ method public void setExact(int, long, android.app.PendingIntent);
+ method public deprecated void setInexactRepeating(int, long, long, android.app.PendingIntent);
method public void setRepeating(int, long, long, android.app.PendingIntent);
method public void setTime(long);
method public void setTimeZone(java.lang.String);
+ method public void setWindow(int, long, long, android.app.PendingIntent);
field public static final int ELAPSED_REALTIME = 3; // 0x3
field public static final int ELAPSED_REALTIME_WAKEUP = 2; // 0x2
- field public static final long INTERVAL_DAY = 86400000L; // 0x5265c00L
- field public static final long INTERVAL_FIFTEEN_MINUTES = 900000L; // 0xdbba0L
- field public static final long INTERVAL_HALF_DAY = 43200000L; // 0x2932e00L
- field public static final long INTERVAL_HALF_HOUR = 1800000L; // 0x1b7740L
- field public static final long INTERVAL_HOUR = 3600000L; // 0x36ee80L
+ field public static final deprecated long INTERVAL_DAY = 86400000L; // 0x5265c00L
+ field public static final deprecated long INTERVAL_FIFTEEN_MINUTES = 900000L; // 0xdbba0L
+ field public static final deprecated long INTERVAL_HALF_DAY = 43200000L; // 0x2932e00L
+ field public static final deprecated long INTERVAL_HALF_HOUR = 1800000L; // 0x1b7740L
+ field public static final deprecated long INTERVAL_HOUR = 3600000L; // 0x36ee80L
field public static final int RTC = 1; // 0x1
field public static final int RTC_WAKEUP = 0; // 0x0
}
@@ -7114,6 +7118,7 @@
field public static final java.lang.String FEATURE_LOCATION_NETWORK = "android.hardware.location.network";
field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
+ field public static final java.lang.String FEATURE_NFC_HCE = "android.hardware.nfc.hce";
field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
field public static final java.lang.String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
field public static final java.lang.String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
@@ -8742,13 +8747,17 @@
method public final boolean isPremultiplied();
method public final boolean isRecycled();
method public void prepareToDraw();
+ method public void reconfigure(int, int, android.graphics.Bitmap.Config);
method public void recycle();
method public boolean sameAs(android.graphics.Bitmap);
+ method public void setConfig(android.graphics.Bitmap.Config);
method public void setDensity(int);
method public void setHasAlpha(boolean);
method public final void setHasMipMap(boolean);
+ method public void setHeight(int);
method public void setPixel(int, int, int);
method public void setPixels(int[], int, int, int, int, int, int);
+ method public void setWidth(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int DENSITY_NONE = 0; // 0x0
@@ -11609,6 +11618,7 @@
field public static final int FX_FOCUS_NAVIGATION_RIGHT = 4; // 0x4
field public static final int FX_FOCUS_NAVIGATION_UP = 1; // 0x1
field public static final int FX_KEYPRESS_DELETE = 7; // 0x7
+ field public static final int FX_KEYPRESS_INVALID = 9; // 0x9
field public static final int FX_KEYPRESS_RETURN = 8; // 0x8
field public static final int FX_KEYPRESS_SPACEBAR = 6; // 0x6
field public static final int FX_KEYPRESS_STANDARD = 5; // 0x5
@@ -13462,6 +13472,7 @@
public class LocalSocket implements java.io.Closeable {
ctor public LocalSocket();
+ ctor public LocalSocket(int);
method public void bind(android.net.LocalSocketAddress) throws java.io.IOException;
method public void close() throws java.io.IOException;
method public void connect(android.net.LocalSocketAddress) throws java.io.IOException;
@@ -13487,6 +13498,9 @@
method public void setSoTimeout(int) throws java.io.IOException;
method public void shutdownInput() throws java.io.IOException;
method public void shutdownOutput() throws java.io.IOException;
+ field public static final int SOCKET_DGRAM = 1; // 0x1
+ field public static final int SOCKET_SEQPACKET = 3; // 0x3
+ field public static final int SOCKET_STREAM = 2; // 0x2
}
public class LocalSocketAddress {
@@ -14731,6 +14745,31 @@
}
+package android.nfc.cardemulation {
+
+ public abstract class HostApduService extends android.app.Service {
+ ctor public HostApduService();
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onDeactivated(int);
+ method public abstract byte[] processCommandApdu(byte[], int);
+ method public final void sendResponseApdu(byte[]);
+ field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
+ field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
+ field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.HostApduService";
+ field public static final java.lang.String SERVICE_META_DATA = "android.nfc.HostApduService";
+ }
+
+ public abstract class SeApduService extends android.app.Service {
+ ctor public SeApduService();
+ method public abstract void onAidSelected(byte[]);
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onHciTransactionEvent(byte[], byte[]);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.nfc.SeApduService";
+ field public static final java.lang.String SERVICE_META_DATA = "android.nfc.SeApduService";
+ }
+
+}
+
package android.nfc.tech {
abstract class BasicTagTechnology implements android.nfc.tech.TagTechnology {
@@ -18549,22 +18588,23 @@
ctor public PrintAttributes.Resolution(java.lang.String, java.lang.CharSequence, int, int);
method public int getHorizontalDpi();
method public java.lang.String getId();
- method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
+ method public java.lang.CharSequence getLabel();
method public int getVerticalDpi();
}
public static final class PrintAttributes.Tray {
ctor public PrintAttributes.Tray(java.lang.String, java.lang.CharSequence);
method public java.lang.String getId();
- method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
+ method public java.lang.CharSequence getLabel();
}
public abstract class PrintDocumentAdapter {
ctor public PrintDocumentAdapter();
method public void onFinish();
- method public abstract void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback);
+ method public abstract void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle);
method public void onStart();
method public abstract void onWrite(java.util.List<android.print.PageRange>, java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
+ field public static final java.lang.String METADATA_KEY_PRINT_PREVIEW = "KEY_METADATA_PRINT_PREVIEW";
}
public static abstract class PrintDocumentAdapter.LayoutResultCallback {
@@ -18616,7 +18656,6 @@
field public static final int PRINT_JOB_ID_UNDEFINED = -1; // 0xffffffff
field public static final int STATE_CANCELED = 6; // 0x6
field public static final int STATE_COMPLETED = 4; // 0x4
- field public static final int STATE_CREATED = 1; // 0x1
field public static final int STATE_FAILED = 5; // 0x5
field public static final int STATE_QUEUED = 2; // 0x2
field public static final int STATE_STARTED = 3; // 0x3
@@ -26768,7 +26807,6 @@
method public float getY();
method public boolean hasFocus();
method public boolean hasFocusable();
- method public boolean hasLayout();
method public boolean hasOnClickListeners();
method public boolean hasOverlappingRendering();
method public boolean hasTransientState();
@@ -26798,6 +26836,7 @@
method public boolean isInEditMode();
method public boolean isInLayout();
method public boolean isInTouchMode();
+ method public boolean isLaidOut();
method public boolean isLayoutDirectionResolved();
method public boolean isLayoutRequested();
method public boolean isLongClickable();
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index bb26443..473b60c 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -24,6 +24,9 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Command that sends key events to the device, either by their keycode, or by
* desired character output.
@@ -31,6 +34,21 @@
public class Input {
private static final String TAG = "Input";
+ private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
+
+ private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
+ put("keyboard", InputDevice.SOURCE_KEYBOARD);
+ put("dpad", InputDevice.SOURCE_DPAD);
+ put("gamepad", InputDevice.SOURCE_GAMEPAD);
+ put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN);
+ put("mouse", InputDevice.SOURCE_MOUSE);
+ put("stylus", InputDevice.SOURCE_STYLUS);
+ put("trackball", InputDevice.SOURCE_TRACKBALL);
+ put("touchpad", InputDevice.SOURCE_TOUCHPAD);
+ put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION);
+ put("joystick", InputDevice.SOURCE_JOYSTICK);
+ }};
+
/**
* Command-line entry point.
@@ -47,86 +65,71 @@
return;
}
- String command = args[0];
+ int index = 0;
+ String command = args[index];
+ int inputSource = InputDevice.SOURCE_UNKNOWN;
+ if (SOURCES.containsKey(command)) {
+ inputSource = SOURCES.get(command);
+ index++;
+ command = args[index];
+ }
+ final int length = args.length - index;
try {
if (command.equals("text")) {
- if (args.length == 2) {
- sendText(args[1]);
+ if (length == 2) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+ sendText(inputSource, args[index+1]);
return;
}
} else if (command.equals("keyevent")) {
- if (args.length >= 2) {
- final boolean longpress = "--longpress".equals(args[1]);
- final int start = longpress ? 2 : 1;
- if (args.length > start) {
- for (int i = start; i < args.length; i++) {
+ if (length >= 2) {
+ final boolean longpress = "--longpress".equals(args[index + 1]);
+ final int start = longpress ? index + 2 : index + 1;
+ inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+ if (length > start) {
+ for (int i = start; i < length; i++) {
int keyCode = KeyEvent.keyCodeFromString(args[i]);
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]);
}
- sendKeyEvent(keyCode, longpress);
+ sendKeyEvent(inputSource, keyCode, longpress);
}
return;
}
}
} else if (command.equals("tap")) {
- if (args.length == 3) {
- sendTap(InputDevice.SOURCE_TOUCHSCREEN, Float.parseFloat(args[1]), Float.parseFloat(args[2]));
+ if (length == 3) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendTap(inputSource, Float.parseFloat(args[index+1]),
+ Float.parseFloat(args[index+2]));
return;
}
} else if (command.equals("swipe")) {
- if (args.length == 5) {
- sendSwipe(InputDevice.SOURCE_TOUCHSCREEN, Float.parseFloat(args[1]), Float.parseFloat(args[2]),
- Float.parseFloat(args[3]), Float.parseFloat(args[4]), -1);
+ int duration = -1;
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ switch (length) {
+ case 6:
+ duration = Integer.parseInt(args[index+5]);
+ case 5:
+ sendSwipe(inputSource,
+ Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
+ Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
+ duration);
+ return;
+ }
+ } else if (command.equals("press")) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+ if (length == 1) {
+ sendTap(inputSource, 0.0f, 0.0f);
return;
}
- } else if (command.equals("touchscreen") || command.equals("touchpad")
- || command.equals("touchnavigation")) {
- // determine input source
- int inputSource = InputDevice.SOURCE_TOUCHSCREEN;
- if (command.equals("touchpad")) {
- inputSource = InputDevice.SOURCE_TOUCHPAD;
- } else if (command.equals("touchnavigation")) {
- inputSource = InputDevice.SOURCE_TOUCH_NAVIGATION;
- }
- // determine subcommand
- if (args.length > 1) {
- String subcommand = args[1];
- if (subcommand.equals("tap")) {
- if (args.length == 4) {
- sendTap(inputSource, Float.parseFloat(args[2]),
- Float.parseFloat(args[3]));
- return;
- }
- } else if (subcommand.equals("swipe")) {
- if (args.length == 6) {
- sendSwipe(inputSource, Float.parseFloat(args[2]),
- Float.parseFloat(args[3]), Float.parseFloat(args[4]),
- Float.parseFloat(args[5]), -1);
- return;
- } else if (args.length == 7) {
- sendSwipe(inputSource, Float.parseFloat(args[2]),
- Float.parseFloat(args[3]), Float.parseFloat(args[4]),
- Float.parseFloat(args[5]), Integer.parseInt(args[6]));
- return;
- }
- }
- }
- } else if (command.equals("trackball")) {
- // determine subcommand
- if (args.length > 1) {
- String subcommand = args[1];
- if (subcommand.equals("press")) {
- sendTap(InputDevice.SOURCE_TRACKBALL, 0.0f, 0.0f);
- return;
- } else if (subcommand.equals("roll")) {
- if (args.length == 4) {
- sendMove(InputDevice.SOURCE_TRACKBALL, Float.parseFloat(args[2]),
- Float.parseFloat(args[3]));
- return;
- }
- }
+ } else if (command.equals("roll")) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+ if (length == 3) {
+ sendMove(inputSource, Float.parseFloat(args[index+1]),
+ Float.parseFloat(args[index+2]));
+ return;
}
} else {
System.err.println("Error: Unknown command: " + command);
@@ -135,7 +138,7 @@
}
} catch (NumberFormatException ex) {
}
- System.err.println("Error: Invalid arguments for command: " + command);
+ System.err.println(INVALID_ARGUMENTS + command);
showUsage();
}
@@ -145,7 +148,7 @@
*
* @param text is a string of characters you want to input to the device.
*/
- private void sendText(String text) {
+ private void sendText(int source, String text) {
StringBuffer buff = new StringBuffer(text);
@@ -157,7 +160,7 @@
buff.setCharAt(i, ' ');
buff.deleteCharAt(--i);
}
- }
+ }
if (buff.charAt(i) == '%') {
escapeFlag = true;
}
@@ -168,21 +171,25 @@
KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
KeyEvent[] events = kcm.getEvents(chars);
for(int i = 0; i < events.length; i++) {
- injectKeyEvent(events[i]);
+ KeyEvent e = events[i];
+ if (source != e.getSource()) {
+ e.setSource(source);
+ }
+ injectKeyEvent(e);
}
}
- private void sendKeyEvent(int keyCode, boolean longpress) {
+ private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) {
long now = SystemClock.uptimeMillis();
injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
if (longpress) {
injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS,
- InputDevice.SOURCE_KEYBOARD));
+ inputSource));
}
injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
}
private void sendTap(int inputSource, float x, float y) {
@@ -206,7 +213,7 @@
lerp(y1, y2, alpha), 1.0f);
now = SystemClock.uptimeMillis();
}
- injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x1, y1, 0.0f);
+ injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
}
/**
@@ -257,14 +264,26 @@
return (b - a) * alpha + a;
}
+ private static final int getSource(int inputSource, int defaultSource) {
+ return inputSource == -1 ? defaultSource : inputSource;
+ }
+
private void showUsage() {
- System.err.println("usage: input ...");
- System.err.println(" input text <string>");
- System.err.println(" input keyevent [--longpress] <key code number or name> ...");
- System.err.println(" input [touchscreen|touchpad|touchnavigation] tap <x> <y>");
- System.err.println(" input [touchscreen|touchpad|touchnavigation] swipe "
- + "<x1> <y1> <x2> <y2> [duration(ms)]");
- System.err.println(" input trackball press");
- System.err.println(" input trackball roll <dx> <dy>");
+ System.err.println("Usage: input [<source>] <command> [<arg>...]");
+ System.err.println();
+ System.err.println("The sources are: ");
+ for (String src : SOURCES.keySet()) {
+ System.err.println(" " + src);
+ }
+ System.err.println();
+ System.err.println("The commands and default sources are:");
+ System.err.println(" text <string> (Default: touchscreen)");
+ System.err.println(" keyevent [--longpress] <key code number or name> ..."
+ + " (Default: keyboard)");
+ System.err.println(" tap <x> <y> (Default: touchscreen)");
+ System.err.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+ + " (Default: touchscreen)");
+ System.err.println(" press (Default: trackball)");
+ System.err.println(" roll <dx> <dy> (Default: trackball)");
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 222ad69..d9f9d61 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -69,6 +69,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.AndroidRuntimeException;
+import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -103,8 +104,6 @@
import java.net.InetAddress;
import java.security.Security;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -164,15 +163,15 @@
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
final H mH = new H();
- final HashMap<IBinder, ActivityClientRecord> mActivities
- = new HashMap<IBinder, ActivityClientRecord>();
+ final ArrayMap<IBinder, ActivityClientRecord> mActivities
+ = new ArrayMap<IBinder, ActivityClientRecord>();
// List of new activities (via ActivityRecord.nextIdle) that should
// be reported when next we idle.
ActivityClientRecord mNewActivities = null;
// Number of activities that are currently visible on-screen.
int mNumVisibleActivities = 0;
- final HashMap<IBinder, Service> mServices
- = new HashMap<IBinder, Service>();
+ final ArrayMap<IBinder, Service> mServices
+ = new ArrayMap<IBinder, Service>();
AppBindData mBoundApplication;
Profiler mProfiler;
int mCurDefaultDisplayDpi;
@@ -183,7 +182,7 @@
final ArrayList<Application> mAllApplications
= new ArrayList<Application>();
// set of instantiated backup agents, keyed by package name
- final HashMap<String, BackupAgent> mBackupAgents = new HashMap<String, BackupAgent>();
+ final ArrayMap<String, BackupAgent> mBackupAgents = new ArrayMap<String, BackupAgent>();
/** Reference to singleton {@link ActivityThread} */
private static ActivityThread sCurrentActivityThread;
Instrumentation mInstrumentation;
@@ -203,10 +202,10 @@
// which means this lock gets held while the activity and window managers
// holds their own lock. Thus you MUST NEVER call back into the activity manager
// or window manager or anything that depends on them while holding this lock.
- final HashMap<String, WeakReference<LoadedApk>> mPackages
- = new HashMap<String, WeakReference<LoadedApk>>();
- final HashMap<String, WeakReference<LoadedApk>> mResourcePackages
- = new HashMap<String, WeakReference<LoadedApk>>();
+ final ArrayMap<String, WeakReference<LoadedApk>> mPackages
+ = new ArrayMap<String, WeakReference<LoadedApk>>();
+ final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages
+ = new ArrayMap<String, WeakReference<LoadedApk>>();
final ArrayList<ActivityClientRecord> mRelaunchingActivities
= new ArrayList<ActivityClientRecord>();
Configuration mPendingConfiguration = null;
@@ -238,17 +237,17 @@
}
// The lock of mProviderMap protects the following variables.
- final HashMap<ProviderKey, ProviderClientRecord> mProviderMap
- = new HashMap<ProviderKey, ProviderClientRecord>();
- final HashMap<IBinder, ProviderRefCount> mProviderRefCountMap
- = new HashMap<IBinder, ProviderRefCount>();
- final HashMap<IBinder, ProviderClientRecord> mLocalProviders
- = new HashMap<IBinder, ProviderClientRecord>();
- final HashMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
- = new HashMap<ComponentName, ProviderClientRecord>();
+ final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
+ = new ArrayMap<ProviderKey, ProviderClientRecord>();
+ final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
+ = new ArrayMap<IBinder, ProviderRefCount>();
+ final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
+ = new ArrayMap<IBinder, ProviderClientRecord>();
+ final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
+ = new ArrayMap<ComponentName, ProviderClientRecord>();
- final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
- = new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
+ final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
+ = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
final GcIdler mGcIdler = new GcIdler();
boolean mGcIdlerScheduled = false;
@@ -3702,47 +3701,45 @@
= new ArrayList<ComponentCallbacks2>();
synchronized (mResourcesManager) {
- final int N = mAllApplications.size();
- for (int i=0; i<N; i++) {
+ final int NAPP = mAllApplications.size();
+ for (int i=0; i<NAPP; i++) {
callbacks.add(mAllApplications.get(i));
}
- if (mActivities.size() > 0) {
- for (ActivityClientRecord ar : mActivities.values()) {
- Activity a = ar.activity;
- if (a != null) {
- Configuration thisConfig = applyConfigCompatMainThread(
- mCurDefaultDisplayDpi, newConfig,
- ar.packageInfo.getCompatibilityInfo());
- if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
- // If the activity is currently resumed, its configuration
- // needs to change right now.
- callbacks.add(a);
- } else if (thisConfig != null) {
- // Otherwise, we will tell it about the change
- // the next time it is resumed or shown. Note that
- // the activity manager may, before then, decide the
- // activity needs to be destroyed to handle its new
- // configuration.
- if (DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Setting activity "
- + ar.activityInfo.name + " newConfig=" + thisConfig);
- }
- ar.newConfig = thisConfig;
+ final int NACT = mActivities.size();
+ for (int i=0; i<NACT; i++) {
+ ActivityClientRecord ar = mActivities.valueAt(i);
+ Activity a = ar.activity;
+ if (a != null) {
+ Configuration thisConfig = applyConfigCompatMainThread(
+ mCurDefaultDisplayDpi, newConfig,
+ ar.packageInfo.getCompatibilityInfo());
+ if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
+ // If the activity is currently resumed, its configuration
+ // needs to change right now.
+ callbacks.add(a);
+ } else if (thisConfig != null) {
+ // Otherwise, we will tell it about the change
+ // the next time it is resumed or shown. Note that
+ // the activity manager may, before then, decide the
+ // activity needs to be destroyed to handle its new
+ // configuration.
+ if (DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Setting activity "
+ + ar.activityInfo.name + " newConfig=" + thisConfig);
}
+ ar.newConfig = thisConfig;
}
}
}
- if (mServices.size() > 0) {
- for (Service service : mServices.values()) {
- callbacks.add(service);
- }
+ final int NSVC = mServices.size();
+ for (int i=0; i<NSVC; i++) {
+ callbacks.add(mServices.valueAt(i));
}
}
synchronized (mProviderMap) {
- if (mLocalProviders.size() > 0) {
- for (ProviderClientRecord providerClientRecord : mLocalProviders.values()) {
- callbacks.add(providerClientRecord.mLocalProvider);
- }
+ final int NPRV = mLocalProviders.size();
+ for (int i=0; i<NPRV; i++) {
+ callbacks.add(mLocalProviders.valueAt(i).mLocalProvider);
}
}
@@ -4575,12 +4572,11 @@
mProviderRefCountMap.remove(jBinder);
}
- Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator();
- while (iter.hasNext()) {
- ProviderClientRecord pr = iter.next();
+ for (int i=mProviderMap.size()-1; i>=0; i--) {
+ ProviderClientRecord pr = mProviderMap.valueAt(i);
IBinder myBinder = pr.mProvider.asBinder();
if (myBinder == jBinder) {
- iter.remove();
+ mProviderMap.removeAt(i);
}
}
}
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 2fe682d..d9c3775 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -16,7 +16,9 @@
package android.app;
+import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.os.RemoteException;
/**
@@ -52,6 +54,8 @@
*/
public class AlarmManager
{
+ private static final String TAG = "AlarmManager";
+
/**
* Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()}
* (wall clock time in UTC), which will wake up the device when
@@ -80,17 +84,33 @@
*/
public static final int ELAPSED_REALTIME = 3;
+ /** @hide */
+ public static final long WINDOW_EXACT = 0;
+ /** @hide */
+ public static final long WINDOW_HEURISTIC = -1;
+
private final IAlarmManager mService;
+ private final boolean mAlwaysExact;
+
/**
* package private on purpose
*/
- AlarmManager(IAlarmManager service) {
+ AlarmManager(IAlarmManager service, Context ctx) {
mService = service;
+
+ final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
+ mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KEY_LIME_PIE);
}
-
+
+ private long legacyExactLength() {
+ return (mAlwaysExact ? WINDOW_EXACT : WINDOW_HEURISTIC);
+ }
+
/**
- * Schedule an alarm. <b>Note: for timing operations (ticks, timeouts,
+ * TBW: discussion of fuzzy nature of alarms in KLP+.
+ *
+ * <p>Schedule an alarm. <b>Note: for timing operations (ticks, timeouts,
* etc) it is easier and much more efficient to use
* {@link android.os.Handler}.</b> If there is already an alarm scheduled
* for the same IntentSender, it will first be canceled.
@@ -122,7 +142,9 @@
* IntentSender.getBroadcast()}.
*
* @see android.os.Handler
+ * @see #setExact
* @see #setRepeating
+ * @see #setWindow
* @see #cancel
* @see android.content.Context#sendBroadcast
* @see android.content.Context#registerReceiver
@@ -133,10 +155,7 @@
* @see #RTC_WAKEUP
*/
public void set(int type, long triggerAtMillis, PendingIntent operation) {
- try {
- mService.set(type, triggerAtMillis, operation);
- } catch (RemoteException ex) {
- }
+ setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation);
}
/**
@@ -177,6 +196,8 @@
*
* @see android.os.Handler
* @see #set
+ * @see #setExact
+ * @see #setWindow
* @see #cancel
* @see android.content.Context#sendBroadcast
* @see android.content.Context#registerReceiver
@@ -188,22 +209,95 @@
*/
public void setRepeating(int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) {
+ setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, operation);
+ }
+
+ /**
+ * Schedule an alarm to be delivered within a given window of time.
+ *
+ * TBW: clean up these docs
+ *
+ * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC or
+ * RTC_WAKEUP.
+ * @param windowStartMillis The earliest time, in milliseconds, that the alarm should
+ * be delivered, expressed in the appropriate clock's units (depending on the alarm
+ * type).
+ * @param windowLengthMillis The length of the requested delivery window,
+ * in milliseconds. The alarm will be delivered no later than this many
+ * milliseconds after the windowStartMillis time. Note that this parameter
+ * is a <i>duration,</i> not the timestamp of the end of the window.
+ * @param operation Action to perform when the alarm goes off;
+ * typically comes from {@link PendingIntent#getBroadcast
+ * IntentSender.getBroadcast()}.
+ *
+ * @see #set
+ * @see #setExact
+ * @see #setRepeating
+ * @see #cancel
+ * @see android.content.Context#sendBroadcast
+ * @see android.content.Context#registerReceiver
+ * @see android.content.Intent#filterEquals
+ * @see #ELAPSED_REALTIME
+ * @see #ELAPSED_REALTIME_WAKEUP
+ * @see #RTC
+ * @see #RTC_WAKEUP
+ */
+ public void setWindow(int type, long windowStartMillis, long windowLengthMillis,
+ PendingIntent operation) {
+ setImpl(type, windowStartMillis, windowLengthMillis, 0, operation);
+ }
+
+ /**
+ * TBW: new 'exact' alarm that must be delivered as nearly as possible
+ * to the precise time specified.
+ */
+ public void setExact(int type, long triggerAtMillis, PendingIntent operation) {
+ setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation);
+ }
+
+ private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
+ PendingIntent operation) {
try {
- mService.setRepeating(type, triggerAtMillis, intervalMillis, operation);
+ mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation);
} catch (RemoteException ex) {
}
}
/**
- * Available inexact recurrence intervals recognized by
- * {@link #setInexactRepeating(int, long, long, PendingIntent)}
+ * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+ * repeating alarms are inexact.
*/
+ @Deprecated
public static final long INTERVAL_FIFTEEN_MINUTES = 15 * 60 * 1000;
+
+ /**
+ * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+ * repeating alarms are inexact.
+ */
+ @Deprecated
public static final long INTERVAL_HALF_HOUR = 2*INTERVAL_FIFTEEN_MINUTES;
+
+ /**
+ * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+ * repeating alarms are inexact.
+ */
+ @Deprecated
public static final long INTERVAL_HOUR = 2*INTERVAL_HALF_HOUR;
+
+ /**
+ * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+ * repeating alarms are inexact.
+ */
+ @Deprecated
public static final long INTERVAL_HALF_DAY = 12*INTERVAL_HOUR;
+
+ /**
+ * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+ * repeating alarms are inexact.
+ */
+ @Deprecated
public static final long INTERVAL_DAY = 2*INTERVAL_HALF_DAY;
-
+
/**
* Schedule a repeating alarm that has inexact trigger time requirements;
* for example, an alarm that repeats every hour, but not necessarily at
@@ -236,6 +330,8 @@
* typically comes from {@link PendingIntent#getBroadcast
* IntentSender.getBroadcast()}.
*
+ * @deprecated As of API 19, all repeating alarms are inexact.
+ *
* @see android.os.Handler
* @see #set
* @see #cancel
@@ -252,12 +348,10 @@
* @see #INTERVAL_HALF_DAY
* @see #INTERVAL_DAY
*/
+ @Deprecated
public void setInexactRepeating(int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) {
- try {
- mService.setInexactRepeating(type, triggerAtMillis, intervalMillis, operation);
- } catch (RemoteException ex) {
- }
+ setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, operation);
}
/**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c22de0c..8d47236 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,6 +16,8 @@
package android.app;
+import android.os.Binder;
+import android.os.IBinder;
import android.util.ArrayMap;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsCallback;
@@ -55,6 +57,8 @@
final ArrayMap<Callback, IAppOpsCallback> mModeWatchers
= new ArrayMap<Callback, IAppOpsCallback>();
+ static IBinder sToken;
+
public static final int MODE_ALLOWED = 0;
public static final int MODE_IGNORED = 1;
public static final int MODE_ERRORED = 2;
@@ -640,6 +644,21 @@
return noteOp(op, Process.myUid(), mContext.getBasePackageName());
}
+ /** @hide */
+ public static IBinder getToken(IAppOpsService service) {
+ synchronized (AppOpsManager.class) {
+ if (sToken != null) {
+ return sToken;
+ }
+ try {
+ sToken = service.getToken(new Binder());
+ } catch (RemoteException e) {
+ // System is dead, whatevs.
+ }
+ return sToken;
+ }
+ }
+
/**
* Report that an application has started executing a long-running operation. Note that you
* must pass in both the uid and name of the application to be checked; this function will
@@ -658,7 +677,7 @@
*/
public int startOp(int op, int uid, String packageName) {
try {
- int mode = mService.startOperation(op, uid, packageName);
+ int mode = mService.startOperation(getToken(mService), op, uid, packageName);
if (mode == MODE_ERRORED) {
throw new SecurityException("Operation not allowed");
}
@@ -674,7 +693,7 @@
*/
public int startOpNoThrow(int op, int uid, String packageName) {
try {
- return mService.startOperation(op, uid, packageName);
+ return mService.startOperation(getToken(mService), op, uid, packageName);
} catch (RemoteException e) {
}
return MODE_IGNORED;
@@ -693,7 +712,7 @@
*/
public void finishOp(int op, int uid, String packageName) {
try {
- mService.finishOperation(op, uid, packageName);
+ mService.finishOperation(getToken(mService), op, uid, packageName);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index a26b88c..413c369 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -17,11 +17,9 @@
package android.app;
import android.os.Trace;
+import android.util.ArrayMap;
import dalvik.system.PathClassLoader;
-import java.util.HashMap;
-import java.util.Map;
-
class ApplicationLoaders
{
public static ApplicationLoaders getDefault()
@@ -71,7 +69,7 @@
}
}
- private final Map<String, ClassLoader> mLoaders = new HashMap<String, ClassLoader>();
+ private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>();
private static final ApplicationLoaders gApplicationLoaders
= new ApplicationLoaders();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 432e9b1..ab2739d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -51,6 +51,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.Display;
@@ -859,26 +860,20 @@
boolean needCleanup = false;
for (String ssp : pkgList) {
synchronized (sSync) {
- if (sIconCache.size() > 0) {
- Iterator<ResourceName> it = sIconCache.keySet().iterator();
- while (it.hasNext()) {
- ResourceName nm = it.next();
- if (nm.packageName.equals(ssp)) {
- //Log.i(TAG, "Removing cached drawable for " + nm);
- it.remove();
- needCleanup = true;
- }
+ for (int i=sIconCache.size()-1; i>=0; i--) {
+ ResourceName nm = sIconCache.keyAt(i);
+ if (nm.packageName.equals(ssp)) {
+ //Log.i(TAG, "Removing cached drawable for " + nm);
+ sIconCache.removeAt(i);
+ needCleanup = true;
}
}
- if (sStringCache.size() > 0) {
- Iterator<ResourceName> it = sStringCache.keySet().iterator();
- while (it.hasNext()) {
- ResourceName nm = it.next();
- if (nm.packageName.equals(ssp)) {
- //Log.i(TAG, "Removing cached string for " + nm);
- it.remove();
- needCleanup = true;
- }
+ for (int i=sStringCache.size()-1; i>=0; i--) {
+ ResourceName nm = sStringCache.keyAt(i);
+ if (nm.packageName.equals(ssp)) {
+ //Log.i(TAG, "Removing cached string for " + nm);
+ sStringCache.removeAt(i);
+ needCleanup = true;
}
}
}
@@ -1335,8 +1330,8 @@
private final IPackageManager mPM;
private static final Object sSync = new Object();
- private static HashMap<ResourceName, WeakReference<Drawable.ConstantState>> sIconCache
- = new HashMap<ResourceName, WeakReference<Drawable.ConstantState>>();
- private static HashMap<ResourceName, WeakReference<CharSequence>> sStringCache
- = new HashMap<ResourceName, WeakReference<CharSequence>>();
+ private static ArrayMap<ResourceName, WeakReference<Drawable.ConstantState>> sIconCache
+ = new ArrayMap<ResourceName, WeakReference<Drawable.ConstantState>>();
+ private static ArrayMap<ResourceName, WeakReference<CharSequence>> sStringCache
+ = new ArrayMap<ResourceName, WeakReference<CharSequence>>();
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6a0fbd5..eeee57d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -16,6 +16,7 @@
package android.app;
+import android.os.Build;
import com.android.internal.policy.PolicyManager;
import com.android.internal.util.Preconditions;
@@ -309,11 +310,11 @@
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
- registerService(ALARM_SERVICE, new StaticServiceFetcher() {
- public Object createStaticService() {
+ registerService(ALARM_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(ALARM_SERVICE);
IAlarmManager service = IAlarmManager.Stub.asInterface(b);
- return new AlarmManager(service);
+ return new AlarmManager(service, ctx);
}});
registerService(AUDIO_SERVICE, new ServiceFetcher() {
@@ -706,6 +707,16 @@
sSharedPrefs.put(packageName, packagePrefs);
}
+ // At least one application in the world actually passes in a null
+ // name. This happened to work because when we generated the file name
+ // we would stringify it to "null.xml". Nice.
+ if (mPackageInfo.getApplicationInfo().targetSdkVersion <
+ Build.VERSION_CODES.KEY_LIME_PIE) {
+ if (name == null) {
+ name = "null";
+ }
+ }
+
sp = packagePrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index edb40ed..0a49ddf 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -24,9 +24,9 @@
* {@hide}
*/
interface IAlarmManager {
- void set(int type, long triggerAtTime, in PendingIntent operation);
- void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
- void setInexactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
+ /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
+ void set(int type, long triggerAtTime, long windowLength,
+ long interval, in PendingIntent operation);
void setTime(long millis);
void setTimeZone(String zone);
void remove(in PendingIntent operation);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 05d3a47..4239a5d 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -16,6 +16,7 @@
package android.app;
+import android.util.ArrayMap;
import com.android.internal.util.ArrayUtils;
import android.content.BroadcastReceiver;
@@ -49,8 +50,6 @@
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
final class IntentReceiverLeaked extends AndroidRuntimeException {
public IntentReceiverLeaked(String msg) {
@@ -89,14 +88,14 @@
private ClassLoader mClassLoader;
private Application mApplication;
- private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mReceivers
- = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
- private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
- = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
- private final HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
- = new HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
- private final HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
- = new HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
+ private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
+ = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
+ private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
+ = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
+ private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
+ = new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
+ private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
+ = new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
int mClientCount = 0;
@@ -540,12 +539,11 @@
public void removeContextRegistrations(Context context,
String who, String what) {
final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
- HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
- mReceivers.remove(context);
+ ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
+ mReceivers.remove(context);
if (rmap != null) {
- Iterator<LoadedApk.ReceiverDispatcher> it = rmap.values().iterator();
- while (it.hasNext()) {
- LoadedApk.ReceiverDispatcher rd = it.next();
+ for (int i=0; i<rmap.size(); i++) {
+ LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
IntentReceiverLeaked leak = new IntentReceiverLeaked(
what + " " + who + " has leaked IntentReceiver "
+ rd.getIntentReceiver() + " that was " +
@@ -566,12 +564,11 @@
}
mUnregisteredReceivers.remove(context);
//Slog.i(TAG, "Receiver registrations: " + mReceivers);
- HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
+ ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
mServices.remove(context);
if (smap != null) {
- Iterator<LoadedApk.ServiceDispatcher> it = smap.values().iterator();
- while (it.hasNext()) {
- LoadedApk.ServiceDispatcher sd = it.next();
+ for (int i=0; i<smap.size(); i++) {
+ LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
what + " " + who + " has leaked ServiceConnection "
+ sd.getServiceConnection() + " that was originally bound here");
@@ -598,7 +595,7 @@
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
- HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
+ ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
if (registered) {
map = mReceivers.get(context);
if (map != null) {
@@ -610,7 +607,7 @@
instrumentation, registered);
if (registered) {
if (map == null) {
- map = new HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
+ map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
}
map.put(r, rd);
@@ -626,7 +623,7 @@
public IIntentReceiver forgetReceiverDispatcher(Context context,
BroadcastReceiver r) {
synchronized (mReceivers) {
- HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
+ ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
LoadedApk.ReceiverDispatcher rd = null;
if (map != null) {
rd = map.get(r);
@@ -636,10 +633,10 @@
mReceivers.remove(context);
}
if (r.getDebugUnregister()) {
- HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
+ ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
= mUnregisteredReceivers.get(context);
if (holder == null) {
- holder = new HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
+ holder = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mUnregisteredReceivers.put(context, holder);
}
RuntimeException ex = new IllegalArgumentException(
@@ -652,7 +649,7 @@
return rd.getIIntentReceiver();
}
}
- HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
+ ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
= mUnregisteredReceivers.get(context);
if (holder != null) {
rd = holder.get(r);
@@ -868,14 +865,14 @@
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
- HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
+ ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
- map = new HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
+ map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mServices.put(context, map);
}
map.put(c, sd);
@@ -889,7 +886,7 @@
public final IServiceConnection forgetServiceDispatcher(Context context,
ServiceConnection c) {
synchronized (mServices) {
- HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> map
+ ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map
= mServices.get(context);
LoadedApk.ServiceDispatcher sd = null;
if (map != null) {
@@ -901,10 +898,10 @@
mServices.remove(context);
}
if ((sd.getFlags()&Context.BIND_DEBUG_UNBIND) != 0) {
- HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
+ ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
= mUnboundServices.get(context);
if (holder == null) {
- holder = new HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
+ holder = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mUnboundServices.put(context, holder);
}
RuntimeException ex = new IllegalArgumentException(
@@ -916,7 +913,7 @@
return sd.getIServiceConnection();
}
}
- HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
+ ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
= mUnboundServices.get(context);
if (holder != null) {
sd = holder.get(c);
@@ -969,8 +966,8 @@
}
}
- private final HashMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
- = new HashMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
+ private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
+ = new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
@@ -1000,9 +997,8 @@
void doForget() {
synchronized(this) {
- Iterator<ServiceDispatcher.ConnectionInfo> it = mActiveConnections.values().iterator();
- while (it.hasNext()) {
- ServiceDispatcher.ConnectionInfo ci = it.next();
+ for (int i=0; i<mActiveConnections.size(); i++) {
+ ServiceDispatcher.ConnectionInfo ci = mActiveConnections.valueAt(i);
ci.binder.unlinkToDeath(ci.deathMonitor, 0);
}
mActiveConnections.clear();
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index e9693dd..f55dba4 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -18,7 +18,6 @@
import static android.app.ActivityThread.DEBUG_CONFIGURATION;
-import android.app.ApplicationPackageManager;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
@@ -34,10 +33,7 @@
import android.view.DisplayAdjustments;
import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.Iterator;
import java.util.Locale;
-import java.util.Map;
/** @hide */
public class ResourcesManager {
@@ -46,8 +42,8 @@
static final boolean DEBUG_STATS = true;
private static ResourcesManager sResourcesManager;
- final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources
- = new HashMap<ResourcesKey, WeakReference<Resources> >();
+ final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources
+ = new ArrayMap<ResourcesKey, WeakReference<Resources> >();
final ArrayMap<DisplayAdjustments, DisplayMetrics> mDefaultDisplayMetrics
= new ArrayMap<DisplayAdjustments, DisplayMetrics>();
@@ -257,19 +253,16 @@
Configuration tmpConfig = null;
- Iterator<Map.Entry<ResourcesKey, WeakReference<Resources>>> it =
- mActiveResources.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<ResourcesKey, WeakReference<Resources>> entry = it.next();
- Resources r = entry.getValue().get();
+ for (int i=mActiveResources.size()-1; i>=0; i--) {
+ ResourcesKey key = mActiveResources.keyAt(i);
+ Resources r = mActiveResources.valueAt(i).get();
if (r != null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ r + " config to: " + config);
- int displayId = entry.getKey().mDisplayId;
+ int displayId = key.mDisplayId;
boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
DisplayMetrics dm = defaultDisplayMetrics;
- ResourcesKey resourcesKey = entry.getKey();
- final boolean hasOverrideConfiguration = resourcesKey.hasOverrideConfiguration();
+ final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
if (!isDefaultDisplay || hasOverrideConfiguration) {
if (tmpConfig == null) {
tmpConfig = new Configuration();
@@ -280,7 +273,7 @@
applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
}
if (hasOverrideConfiguration) {
- tmpConfig.updateFrom(resourcesKey.mOverrideConfiguration);
+ tmpConfig.updateFrom(key.mOverrideConfiguration);
}
r.updateConfiguration(tmpConfig, dm, compat);
} else {
@@ -290,7 +283,7 @@
// + " " + r + ": " + r.getConfiguration());
} else {
//Slog.i(TAG, "Removing old resources " + v.getKey());
- it.remove();
+ mActiveResources.removeAt(i);
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 62c8d58..a954f59 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -16,6 +16,7 @@
package android.content;
+import android.util.ArraySet;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -42,7 +43,6 @@
import java.io.Serializable;
import java.net.URISyntaxException;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Set;
@@ -3609,7 +3609,7 @@
private String mPackage;
private ComponentName mComponent;
private int mFlags;
- private HashSet<String> mCategories;
+ private ArraySet<String> mCategories;
private Bundle mExtras;
private Rect mSourceBounds;
private Intent mSelector;
@@ -3634,7 +3634,7 @@
this.mComponent = o.mComponent;
this.mFlags = o.mFlags;
if (o.mCategories != null) {
- this.mCategories = new HashSet<String>(o.mCategories);
+ this.mCategories = new ArraySet<String>(o.mCategories);
}
if (o.mExtras != null) {
this.mExtras = new Bundle(o.mExtras);
@@ -3662,7 +3662,7 @@
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
if (o.mCategories != null) {
- this.mCategories = new HashSet<String>(o.mCategories);
+ this.mCategories = new ArraySet<String>(o.mCategories);
}
}
@@ -5226,7 +5226,7 @@
*/
public Intent addCategory(String category) {
if (mCategories == null) {
- mCategories = new HashSet<String>();
+ mCategories = new ArraySet<String>();
}
mCategories.add(category.intern());
return this;
@@ -6370,7 +6370,7 @@
if (other.mCategories != null
&& (mCategories == null || (flags&FILL_IN_CATEGORIES) != 0)) {
if (other.mCategories != null) {
- mCategories = new HashSet<String>(other.mCategories);
+ mCategories = new ArraySet<String>(other.mCategories);
}
changes |= FILL_IN_CATEGORIES;
}
@@ -6641,12 +6641,9 @@
}
first = false;
b.append("cat=[");
- Iterator<String> i = mCategories.iterator();
- boolean didone = false;
- while (i.hasNext()) {
- if (didone) b.append(",");
- didone = true;
- b.append(i.next());
+ for (int i=0; i<mCategories.size(); i++) {
+ if (i > 0) b.append(',');
+ b.append(mCategories.valueAt(i));
}
b.append("]");
}
@@ -6804,8 +6801,8 @@
uri.append("action=").append(Uri.encode(mAction)).append(';');
}
if (mCategories != null) {
- for (String category : mCategories) {
- uri.append("category=").append(Uri.encode(category)).append(';');
+ for (int i=0; i<mCategories.size(); i++) {
+ uri.append("category=").append(Uri.encode(mCategories.valueAt(i))).append(';');
}
}
if (mType != null) {
@@ -6873,9 +6870,10 @@
}
if (mCategories != null) {
- out.writeInt(mCategories.size());
- for (String category : mCategories) {
- out.writeString(category);
+ final int N = mCategories.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ out.writeString(mCategories.valueAt(i));
}
} else {
out.writeInt(0);
@@ -6927,7 +6925,7 @@
int N = in.readInt();
if (N > 0) {
- mCategories = new HashSet<String>();
+ mCategories = new ArraySet<String>();
int i;
for (i=0; i<N; i++) {
mCategories.add(in.readString().intern());
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8a8751e..81f860e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -955,6 +955,14 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports host-
+ * based NFC card emulation.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_NFC_HCE = "android.hardware.nfc.hce";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device includes an accelerometer.
*/
@SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 810a521..ce7addc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1554,10 +1554,15 @@
int type;
PublicKey currentKey = null;
+ int currentKeyDepth = -1;
Map<PublicKey, Set<String>> definedKeySets = new HashMap<PublicKey, Set<String>>();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG) {
+ if (parser.getDepth() == currentKeyDepth) {
+ currentKey = null;
+ currentKeyDepth = -1;
+ }
continue;
}
String tagname = parser.getName();
@@ -1567,9 +1572,21 @@
final String encodedKey = sa.getNonResourceString(
com.android.internal.R.styleable.PublicKey_value);
currentKey = parsePublicKey(encodedKey);
+ if (currentKey == null) {
+ Slog.w(TAG, "No valid key in 'publicKey' tag at "
+ + parser.getPositionDescription());
+ sa.recycle();
+ continue;
+ }
+ currentKeyDepth = parser.getDepth();
definedKeySets.put(currentKey, new HashSet<String>());
sa.recycle();
} else if (tagname.equals("keyset")) {
+ if (currentKey == null) {
+ Slog.i(TAG, "'keyset' not in 'publicKey' tag at "
+ + parser.getPositionDescription());
+ continue;
+ }
final TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.KeySet);
final String name = sa.getNonResourceString(
diff --git a/core/java/android/hardware/photography/CameraMetadata.java b/core/java/android/hardware/photography/CameraMetadata.java
index 4633b2f..c024c05 100644
--- a/core/java/android/hardware/photography/CameraMetadata.java
+++ b/core/java/android/hardware/photography/CameraMetadata.java
@@ -16,6 +16,10 @@
package android.hardware.photography;
+import android.hardware.photography.impl.MetadataMarshalClass;
+import android.hardware.photography.impl.MetadataMarshalRect;
+import android.hardware.photography.impl.MetadataMarshalSize;
+import android.hardware.photography.impl.MetadataMarshalString;
import android.os.Parcelable;
import android.os.Parcel;
import android.util.Log;
@@ -85,6 +89,11 @@
public <T> void set(Key<T> key, T value) {
int tag = key.getTag();
+ if (value == null) {
+ writeValues(tag, null);
+ return;
+ }
+
int nativeType = getNativeType(tag);
int size = packSingle(value, null, key.mType, nativeType, /* sizeOnly */true);
@@ -265,6 +274,11 @@
private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType,
boolean sizeOnly) {
+ MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
+ if (marshaler != null) {
+ return marshaler.marshal(value, buffer, nativeType, sizeOnly);
+ }
+
/**
* FIXME: This doesn't actually work because getFields() returns fields in an unordered
* manner. Although we could sort and get the data to come out correctly on the *java* side,
@@ -558,6 +572,11 @@
private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) {
+ MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
+ if (marshaler != null) {
+ return marshaler.unmarshal(buffer, nativeType);
+ }
+
/**
* FIXME: This doesn't actually work because getFields() returns fields in an unordered
* manner. Although we could sort and get the data to come out correctly on the *java* side,
@@ -611,14 +630,44 @@
Class<?> componentType = type.getComponentType();
Object array;
- int remaining = buffer.remaining();
- // FIXME: Assumes that the rest of the ByteBuffer is part of the array.
- int arraySize = remaining / getTypeSize(nativeType);
+ int elementSize = getTypeSize(nativeType);
- array = Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; ++i) {
- Object elem = unpackSingle(buffer, componentType, nativeType);
- Array.set(array, i, elem);
+ MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType);
+ if (marshaler != null) {
+ elementSize = marshaler.getNativeSize(nativeType);
+ }
+
+ if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) {
+ int remaining = buffer.remaining();
+ int arraySize = remaining / elementSize;
+
+ Log.v(TAG,
+ String.format(
+ "Attempting to unpack array (count = %d, element size = %d, bytes " +
+ "remaining = %d) for type %s",
+ arraySize, elementSize, remaining, type));
+
+ array = Array.newInstance(componentType, arraySize);
+ for (int i = 0; i < arraySize; ++i) {
+ Object elem = unpackSingle(buffer, componentType, nativeType);
+ Array.set(array, i, elem);
+ }
+ } else {
+ // Dynamic size, use an array list.
+ ArrayList<Object> arrayList = new ArrayList<Object>();
+
+ int primitiveSize = getTypeSize(nativeType);
+ while (buffer.remaining() >= primitiveSize) {
+ Object elem = unpackSingle(buffer, componentType, nativeType);
+ arrayList.add(elem);
+ }
+
+ array = arrayList.toArray((T[]) Array.newInstance(componentType, 0));
+ }
+
+ if (buffer.remaining() != 0) {
+ Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
+ + type);
}
return (T) array;
@@ -927,11 +976,39 @@
return values[ordinal];
}
+ static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new
+ HashMap<Class<?>, MetadataMarshalClass<?>>();
+
+ private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) {
+ sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) {
+ MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type);
+
+ if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) {
+ throw new UnsupportedOperationException("Unsupported type " + nativeType +
+ " to be marshalled to/from a " + type);
+ }
+
+ return marshaler;
+ }
+
/**
* We use a class initializer to allow the native code to cache some field offsets
*/
static {
System.loadLibrary("media_jni");
nativeClassInit();
+
+ Log.v(TAG, "Shall register metadata marshalers");
+
+ // load built-in marshallers
+ registerMarshaler(new MetadataMarshalRect());
+ registerMarshaler(new MetadataMarshalSize());
+ registerMarshaler(new MetadataMarshalString());
+
+ Log.v(TAG, "Registered metadata marshalers");
}
}
diff --git a/core/java/android/hardware/photography/CameraPropertiesKeys.java b/core/java/android/hardware/photography/CameraPropertiesKeys.java
index f511ae7..db8ab44 100644
--- a/core/java/android/hardware/photography/CameraPropertiesKeys.java
+++ b/core/java/android/hardware/photography/CameraPropertiesKeys.java
@@ -71,8 +71,8 @@
}
public static final class Jpeg {
- public static final Key<int[]> AVAILABLE_THUMBNAIL_SIZES =
- new Key<int[]>("android.jpeg.availableThumbnailSizes", int[].class);
+ public static final Key<android.hardware.photography.Size[]> AVAILABLE_THUMBNAIL_SIZES =
+ new Key<android.hardware.photography.Size[]>("android.jpeg.availableThumbnailSizes", android.hardware.photography.Size[].class);
}
@@ -90,8 +90,8 @@
new Key<Float>("android.lens.info.hyperfocalDistance", float.class);
public static final Key<Float> MINIMUM_FOCUS_DISTANCE =
new Key<Float>("android.lens.info.minimumFocusDistance", float.class);
- public static final Key<int[]> SHADING_MAP_SIZE =
- new Key<int[]>("android.lens.info.shadingMapSize", int[].class);
+ public static final Key<android.hardware.photography.Size> SHADING_MAP_SIZE =
+ new Key<android.hardware.photography.Size>("android.lens.info.shadingMapSize", android.hardware.photography.Size.class);
}
public static final class FacingKey extends Key<Lens.FacingKey.Enum> {
@@ -161,29 +161,29 @@
new AvailableFormatsKey("android.scaler.availableFormats");
public static final Key<long[]> AVAILABLE_JPEG_MIN_DURATIONS =
new Key<long[]>("android.scaler.availableJpegMinDurations", long[].class);
- public static final Key<int[]> AVAILABLE_JPEG_SIZES =
- new Key<int[]>("android.scaler.availableJpegSizes", int[].class);
+ public static final Key<android.hardware.photography.Size[]> AVAILABLE_JPEG_SIZES =
+ new Key<android.hardware.photography.Size[]>("android.scaler.availableJpegSizes", android.hardware.photography.Size[].class);
public static final Key<Float> AVAILABLE_MAX_DIGITAL_ZOOM =
new Key<Float>("android.scaler.availableMaxDigitalZoom", float.class);
public static final Key<long[]> AVAILABLE_PROCESSED_MIN_DURATIONS =
new Key<long[]>("android.scaler.availableProcessedMinDurations", long[].class);
- public static final Key<int[]> AVAILABLE_PROCESSED_SIZES =
- new Key<int[]>("android.scaler.availableProcessedSizes", int[].class);
+ public static final Key<android.hardware.photography.Size[]> AVAILABLE_PROCESSED_SIZES =
+ new Key<android.hardware.photography.Size[]>("android.scaler.availableProcessedSizes", android.hardware.photography.Size[].class);
}
public static final class Sensor {
public static final class Info {
- public static final Key<int[]> ACTIVE_ARRAY_SIZE =
- new Key<int[]>("android.sensor.info.activeArraySize", int[].class);
- public static final Key<int[]> AVAILABLE_SENSITIVITIES =
- new Key<int[]>("android.sensor.info.availableSensitivities", int[].class);
+ public static final Key<android.graphics.Rect> ACTIVE_ARRAY_SIZE =
+ new Key<android.graphics.Rect>("android.sensor.info.activeArraySize", android.graphics.Rect.class);
+ public static final Key<int[]> SENSITIVITY_RANGE =
+ new Key<int[]>("android.sensor.info.sensitivityRange", int[].class);
public static final Key<long[]> EXPOSURE_TIME_RANGE =
new Key<long[]>("android.sensor.info.exposureTimeRange", long[].class);
public static final Key<Long> MAX_FRAME_DURATION =
new Key<Long>("android.sensor.info.maxFrameDuration", long.class);
- public static final Key<float[]> PHYSICAL_SIZE =
- new Key<float[]>("android.sensor.info.physicalSize", float[].class);
+ public static final Key<android.hardware.photography.Size> PHYSICAL_SIZE =
+ new Key<android.hardware.photography.Size>("android.sensor.info.physicalSize", android.hardware.photography.Size.class);
}
public static final Key<Rational> BASE_GAIN_FACTOR =
new Key<Rational>("android.sensor.baseGainFactor", Rational.class);
diff --git a/core/java/android/hardware/photography/CaptureRequestKeys.java b/core/java/android/hardware/photography/CaptureRequestKeys.java
index b23e71d..ca6d487 100644
--- a/core/java/android/hardware/photography/CaptureRequestKeys.java
+++ b/core/java/android/hardware/photography/CaptureRequestKeys.java
@@ -60,8 +60,10 @@
public static final Key<ColorCorrection.ModeKey.Enum> MODE =
new ModeKey("android.colorCorrection.mode");
- public static final Key<float[]> TRANSFORM =
- new Key<float[]>("android.colorCorrection.transform", float[].class);
+ public static final Key<Rational[]> TRANSFORM =
+ new Key<Rational[]>("android.colorCorrection.transform", Rational[].class);
+ public static final Key<float[]> GAINS =
+ new Key<float[]>("android.colorCorrection.gains", float[].class);
}
@@ -91,25 +93,8 @@
new AeAntibandingModeKey("android.control.aeAntibandingMode");
public static final Key<Integer> AE_EXPOSURE_COMPENSATION =
new Key<Integer>("android.control.aeExposureCompensation", int.class);
-
- public static final class AeLockKey extends Key<Control.AeLockKey.Enum> {
- public enum Enum {
- OFF,
- ON;
- }
-
- public static final Enum OFF = Enum.OFF;
- public static final Enum ON = Enum.ON;
-
- // TODO: remove requirement for constructor by making Key an interface
- private AeLockKey(String name) {
- super(name, Control.AeLockKey.Enum.class);
- }
-
- }
-
- public static final Key<Control.AeLockKey.Enum> AE_LOCK =
- new AeLockKey("android.control.aeLock");
+ public static final Key<Boolean> AE_LOCK =
+ new Key<Boolean>("android.control.aeLock", boolean.class);
public static final class AeModeKey extends Key<Control.AeModeKey.Enum> {
public enum Enum {
@@ -208,25 +193,8 @@
public static final Key<Control.AfTriggerKey.Enum> AF_TRIGGER =
new AfTriggerKey("android.control.afTrigger");
-
- public static final class AwbLockKey extends Key<Control.AwbLockKey.Enum> {
- public enum Enum {
- OFF,
- ON;
- }
-
- public static final Enum OFF = Enum.OFF;
- public static final Enum ON = Enum.ON;
-
- // TODO: remove requirement for constructor by making Key an interface
- private AwbLockKey(String name) {
- super(name, Control.AwbLockKey.Enum.class);
- }
-
- }
-
- public static final Key<Control.AwbLockKey.Enum> AWB_LOCK =
- new AwbLockKey("android.control.awbLock");
+ public static final Key<Boolean> AWB_LOCK =
+ new Key<Boolean>("android.control.awbLock", boolean.class);
public static final class AwbModeKey extends Key<Control.AwbModeKey.Enum> {
public enum Enum {
@@ -413,25 +381,8 @@
public static final Key<Control.SceneModeKey.Enum> SCENE_MODE =
new SceneModeKey("android.control.sceneMode");
-
- public static final class VideoStabilizationModeKey extends Key<Control.VideoStabilizationModeKey.Enum> {
- public enum Enum {
- OFF,
- ON;
- }
-
- public static final Enum OFF = Enum.OFF;
- public static final Enum ON = Enum.ON;
-
- // TODO: remove requirement for constructor by making Key an interface
- private VideoStabilizationModeKey(String name) {
- super(name, Control.VideoStabilizationModeKey.Enum.class);
- }
-
- }
-
- public static final Key<Control.VideoStabilizationModeKey.Enum> VIDEO_STABILIZATION_MODE =
- new VideoStabilizationModeKey("android.control.videoStabilizationMode");
+ public static final Key<Boolean> VIDEO_STABILIZATION_MODE =
+ new Key<Boolean>("android.control.videoStabilizationMode", boolean.class);
}
@@ -488,8 +439,8 @@
public static final class Jpeg {
public static final Key<double[]> GPS_COORDINATES =
new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
- public static final Key<Byte> GPS_PROCESSING_METHOD =
- new Key<Byte>("android.jpeg.gpsProcessingMethod", byte.class);
+ public static final Key<String> GPS_PROCESSING_METHOD =
+ new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
public static final Key<Long> GPS_TIMESTAMP =
new Key<Long>("android.jpeg.gpsTimestamp", long.class);
public static final Key<Integer> ORIENTATION =
@@ -498,8 +449,8 @@
new Key<Byte>("android.jpeg.quality", byte.class);
public static final Key<Byte> THUMBNAIL_QUALITY =
new Key<Byte>("android.jpeg.thumbnailQuality", byte.class);
- public static final Key<int[]> THUMBNAIL_SIZE =
- new Key<int[]>("android.jpeg.thumbnailSize", int[].class);
+ public static final Key<android.hardware.photography.Size> THUMBNAIL_SIZE =
+ new Key<android.hardware.photography.Size>("android.jpeg.thumbnailSize", android.hardware.photography.Size.class);
}
@@ -572,8 +523,8 @@
}
public static final class Scaler {
- public static final Key<int[]> CROP_REGION =
- new Key<int[]>("android.scaler.cropRegion", int[].class);
+ public static final Key<android.graphics.Rect> CROP_REGION =
+ new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
}
@@ -647,31 +598,17 @@
* @hide
*/
public static final class Led {
-
- /**
- * @hide
- */
- public static final class TransmitKey extends Key<Led.TransmitKey.Enum> {
- public enum Enum {
- OFF,
- ON;
- }
-
- public static final Enum OFF = Enum.OFF;
- public static final Enum ON = Enum.ON;
-
- // TODO: remove requirement for constructor by making Key an interface
- private TransmitKey(String name) {
- super(name, Led.TransmitKey.Enum.class);
- }
-
- }
-
/**
* @hide
*/
- public static final Key<Led.TransmitKey.Enum> TRANSMIT =
- new TransmitKey("android.led.transmit");
+ public static final Key<Boolean> TRANSMIT =
+ new Key<Boolean>("android.led.transmit", boolean.class);
+
+ }
+
+ public static final class BlackLevel {
+ public static final Key<Boolean> LOCK =
+ new Key<Boolean>("android.blackLevel.lock", boolean.class);
}
diff --git a/core/java/android/hardware/photography/CaptureResultKeys.java b/core/java/android/hardware/photography/CaptureResultKeys.java
index e44fc91..4931564 100644
--- a/core/java/android/hardware/photography/CaptureResultKeys.java
+++ b/core/java/android/hardware/photography/CaptureResultKeys.java
@@ -39,27 +39,10 @@
**/
public final class CaptureResultKeys {
public static final class ColorCorrection {
-
- public static final class ModeKey extends Key<ColorCorrection.ModeKey.Enum> {
- public enum Enum {
- TRANSFORM_MATRIX,
- FAST,
- HIGH_QUALITY;
- }
-
- public static final Enum TRANSFORM_MATRIX = Enum.TRANSFORM_MATRIX;
- public static final Enum FAST = Enum.FAST;
- public static final Enum HIGH_QUALITY = Enum.HIGH_QUALITY;
-
- // TODO: remove requirement for constructor by making Key an interface
- private ModeKey(String name) {
- super(name, ColorCorrection.ModeKey.Enum.class);
- }
-
- }
-
- public static final Key<ColorCorrection.ModeKey.Enum> MODE =
- new ModeKey("android.colorCorrection.mode");
+ public static final Key<Rational[]> TRANSFORM =
+ new Key<Rational[]>("android.colorCorrection.transform", Rational[].class);
+ public static final Key<float[]> GAINS =
+ new Key<float[]>("android.colorCorrection.gains", float[].class);
}
@@ -317,8 +300,8 @@
public static final class Jpeg {
public static final Key<double[]> GPS_COORDINATES =
new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
- public static final Key<Byte> GPS_PROCESSING_METHOD =
- new Key<Byte>("android.jpeg.gpsProcessingMethod", byte.class);
+ public static final Key<String> GPS_PROCESSING_METHOD =
+ new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
public static final Key<Long> GPS_TIMESTAMP =
new Key<Long>("android.jpeg.gpsTimestamp", long.class);
public static final Key<Integer> ORIENTATION =
@@ -327,8 +310,8 @@
new Key<Byte>("android.jpeg.quality", byte.class);
public static final Key<Byte> THUMBNAIL_QUALITY =
new Key<Byte>("android.jpeg.thumbnailQuality", byte.class);
- public static final Key<int[]> THUMBNAIL_SIZE =
- new Key<int[]>("android.jpeg.thumbnailSize", int[].class);
+ public static final Key<android.hardware.photography.Size> THUMBNAIL_SIZE =
+ new Key<android.hardware.photography.Size>("android.jpeg.thumbnailSize", android.hardware.photography.Size.class);
}
@@ -419,8 +402,8 @@
}
public static final class Scaler {
- public static final Key<int[]> CROP_REGION =
- new Key<int[]>("android.scaler.cropRegion", int[].class);
+ public static final Key<android.graphics.Rect> CROP_REGION =
+ new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
}
@@ -462,10 +445,37 @@
new Key<int[]>("android.statistics.faceIds", int[].class);
public static final Key<int[]> FACE_LANDMARKS =
new Key<int[]>("android.statistics.faceLandmarks", int[].class);
- public static final Key<int[]> FACE_RECTANGLES =
- new Key<int[]>("android.statistics.faceRectangles", int[].class);
+ public static final Key<android.graphics.Rect[]> FACE_RECTANGLES =
+ new Key<android.graphics.Rect[]>("android.statistics.faceRectangles", android.graphics.Rect[].class);
public static final Key<byte[]> FACE_SCORES =
new Key<byte[]>("android.statistics.faceScores", byte[].class);
+ public static final Key<float[]> LENS_SHADING_MAP =
+ new Key<float[]>("android.statistics.lensShadingMap", float[].class);
+ public static final Key<float[]> PREDICTED_COLOR_GAINS =
+ new Key<float[]>("android.statistics.predictedColorGains", float[].class);
+ public static final Key<Rational[]> PREDICTED_COLOR_TRANSFORM =
+ new Key<Rational[]>("android.statistics.predictedColorTransform", Rational[].class);
+
+ public static final class SceneFlickerKey extends Key<Statistics.SceneFlickerKey.Enum> {
+ public enum Enum {
+ NONE,
+ _50HZ,
+ _60HZ;
+ }
+
+ public static final Enum NONE = Enum.NONE;
+ public static final Enum _50HZ = Enum._50HZ;
+ public static final Enum _60HZ = Enum._60HZ;
+
+ // TODO: remove requirement for constructor by making Key an interface
+ private SceneFlickerKey(String name) {
+ super(name, Statistics.SceneFlickerKey.Enum.class);
+ }
+
+ }
+
+ public static final Key<Statistics.SceneFlickerKey.Enum> SCENE_FLICKER =
+ new SceneFlickerKey("android.statistics.sceneFlicker");
}
@@ -504,31 +514,17 @@
* @hide
*/
public static final class Led {
-
- /**
- * @hide
- */
- public static final class TransmitKey extends Key<Led.TransmitKey.Enum> {
- public enum Enum {
- OFF,
- ON;
- }
-
- public static final Enum OFF = Enum.OFF;
- public static final Enum ON = Enum.ON;
-
- // TODO: remove requirement for constructor by making Key an interface
- private TransmitKey(String name) {
- super(name, Led.TransmitKey.Enum.class);
- }
-
- }
-
/**
* @hide
*/
- public static final Key<Led.TransmitKey.Enum> TRANSMIT =
- new TransmitKey("android.led.transmit");
+ public static final Key<Boolean> TRANSMIT =
+ new Key<Boolean>("android.led.transmit", boolean.class);
+
+ }
+
+ public static final class BlackLevel {
+ public static final Key<Boolean> LOCK =
+ new Key<Boolean>("android.blackLevel.lock", boolean.class);
}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalClass.java b/core/java/android/hardware/photography/impl/MetadataMarshalClass.java
new file mode 100644
index 0000000..a70784d
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalClass.java
@@ -0,0 +1,67 @@
+/*
+ * 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.hardware.photography.impl;
+
+import java.nio.ByteBuffer;
+
+public interface MetadataMarshalClass<T> {
+
+ /**
+ * Marshal the specified object instance (value) into a byte buffer.
+ *
+ * @param value the value of type T that we wish to write into the byte buffer
+ * @param buffer the byte buffer into which the marshalled object will be written
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
+ * Guaranteed to be one for which isNativeTypeSupported returns true.
+ * @param sizeOnly if this is true, don't write to the byte buffer. calculate the size only.
+ * @return the size that needs to be written to the byte buffer
+ */
+ int marshal(T value, ByteBuffer buffer, int nativeType, boolean sizeOnly);
+
+ /**
+ * Unmarshal a new object instance from the byte buffer.
+ * @param buffer the byte buffer, from which we will read the object
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
+ * Guaranteed to be one for which isNativeTypeSupported returns true.
+ * @return a new instance of type T read from the byte buffer
+ */
+ T unmarshal(ByteBuffer buffer, int nativeType);
+
+ Class<T> getMarshalingClass();
+
+ /**
+ * Determines whether or not this marshaller supports this native type. Most marshallers
+ * will are likely to only support one type.
+ *
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}
+ * @return true if it supports, false otherwise
+ */
+ boolean isNativeTypeSupported(int nativeType);
+
+ public static int NATIVE_SIZE_DYNAMIC = -1;
+
+ /**
+ * How many bytes T will take up if marshalled to/from nativeType
+ * @param nativeType the native type, e.g.
+ * {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}
+ * @return a size in bytes, or NATIVE_SIZE_DYNAMIC if the size is dynamic
+ */
+ int getNativeSize(int nativeType);
+}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalRect.java b/core/java/android/hardware/photography/impl/MetadataMarshalRect.java
new file mode 100644
index 0000000..d6636ac
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalRect.java
@@ -0,0 +1,68 @@
+/*
+ * 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.hardware.photography.impl;
+
+import android.graphics.Rect;
+import android.hardware.photography.CameraMetadata;
+
+import java.nio.ByteBuffer;
+
+public class MetadataMarshalRect implements MetadataMarshalClass<Rect> {
+ private static final int SIZE = 16;
+
+ @Override
+ public int marshal(Rect value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
+ if (sizeOnly) {
+ return SIZE;
+ }
+
+ buffer.putInt(value.left);
+ buffer.putInt(value.top);
+ buffer.putInt(value.width());
+ buffer.putInt(value.height());
+
+ return SIZE;
+ }
+
+ @Override
+ public Rect unmarshal(ByteBuffer buffer, int nativeType) {
+
+ int left = buffer.getInt();
+ int top = buffer.getInt();
+ int width = buffer.getInt();
+ int height = buffer.getInt();
+
+ int right = left + width;
+ int bottom = top + height;
+
+ return new Rect(left, top, right, bottom);
+ }
+
+ @Override
+ public Class<Rect> getMarshalingClass() {
+ return Rect.class;
+ }
+
+ @Override
+ public boolean isNativeTypeSupported(int nativeType) {
+ return nativeType == CameraMetadata.TYPE_INT32;
+ }
+
+ @Override
+ public int getNativeSize(int nativeType) {
+ return SIZE;
+ }
+}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalSize.java b/core/java/android/hardware/photography/impl/MetadataMarshalSize.java
new file mode 100644
index 0000000..430219c
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalSize.java
@@ -0,0 +1,61 @@
+/*
+ * 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.hardware.photography.impl;
+
+import android.hardware.photography.CameraMetadata;
+import android.hardware.photography.Size;
+
+import java.nio.ByteBuffer;
+
+public class MetadataMarshalSize implements MetadataMarshalClass<Size> {
+
+ private static final int SIZE = 8;
+
+ @Override
+ public int marshal(Size value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
+ if (sizeOnly) {
+ return SIZE;
+ }
+
+ buffer.putInt(value.getWidth());
+ buffer.putInt(value.getHeight());
+
+ return SIZE;
+ }
+
+ @Override
+ public Size unmarshal(ByteBuffer buffer, int nativeType) {
+ int width = buffer.getInt();
+ int height = buffer.getInt();
+
+ return new Size(width, height);
+ }
+
+ @Override
+ public Class<Size> getMarshalingClass() {
+ return Size.class;
+ }
+
+ @Override
+ public boolean isNativeTypeSupported(int nativeType) {
+ return nativeType == CameraMetadata.TYPE_INT32;
+ }
+
+ @Override
+ public int getNativeSize(int nativeType) {
+ return SIZE;
+ }
+}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalString.java b/core/java/android/hardware/photography/impl/MetadataMarshalString.java
new file mode 100644
index 0000000..81123ee
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalString.java
@@ -0,0 +1,81 @@
+/*
+ * 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.hardware.photography.impl;
+
+import android.hardware.photography.CameraMetadata;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+public class MetadataMarshalString implements MetadataMarshalClass<String> {
+
+ private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+ @Override
+ public int marshal(String value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
+ byte[] arr = value.getBytes(UTF8_CHARSET);
+
+ if (!sizeOnly) {
+ buffer.put(arr);
+ buffer.put((byte)0); // metadata strings are NULL-terminated
+ }
+
+ return arr.length + 1;
+ }
+
+ @Override
+ public String unmarshal(ByteBuffer buffer, int nativeType) {
+
+ buffer.mark(); // save the current position
+
+ boolean foundNull = false;
+ int stringLength = 0;
+ while (buffer.hasRemaining()) {
+ if (buffer.get() == (byte)0) {
+ foundNull = true;
+ break;
+ }
+
+ stringLength++;
+ }
+ if (!foundNull) {
+ throw new IllegalArgumentException("Strings must be null-terminated");
+ }
+
+ buffer.reset(); // go back to the previously marked position
+
+ byte[] strBytes = new byte[stringLength + 1];
+ buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character
+
+ // not including null character
+ return new String(strBytes, /*offset*/0, stringLength, UTF8_CHARSET);
+ }
+
+ @Override
+ public Class<String> getMarshalingClass() {
+ return String.class;
+ }
+
+ @Override
+ public boolean isNativeTypeSupported(int nativeType) {
+ return nativeType == CameraMetadata.TYPE_BYTE;
+ }
+
+ @Override
+ public int getNativeSize(int nativeType) {
+ return NATIVE_SIZE_DYNAMIC;
+ }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 04ab0a5..f920874 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1373,4 +1373,16 @@
}
return timeOutMs;
}
+
+ /**
+ * Get the carrier provisioning url.
+ * {@hide}
+ */
+ public String getMobileProvisioningUrl() {
+ try {
+ return mService.getMobileProvisioningUrl();
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
}
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 1ebf393..5151a04 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -374,7 +374,7 @@
//Do it a bit earlier than half the lease duration time
//to beat the native DHCP client and avoid extra packets
//48% for one hour lease time = 29 minutes
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() +
leaseDuration * 480, //in milliseconds
mDhcpRenewalIntent);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 7ee10c3..d6a3e37 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -37,6 +37,9 @@
/** {@hide} */
interface IConnectivityManager
{
+ // Keep this in sync with framework/native/services/connectivitymanager/ConnectivityManager.h
+ void markSocketAsUser(in ParcelFileDescriptor socket, int uid);
+
void setNetworkPreference(int pref);
int getNetworkPreference();
@@ -119,8 +122,6 @@
boolean prepareVpn(String oldPackage, String newPackage);
- void markSocketAsUser(in ParcelFileDescriptor socket, int uid);
-
ParcelFileDescriptor establishVpn(in VpnConfig config);
void startLegacyVpn(in VpnProfile profile);
@@ -136,4 +137,6 @@
int findConnectionTypeForIface(in String iface);
int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs, in ResultReceiver resultReceiver);
+
+ String getMobileProvisioningUrl();
}
diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java
index 2b93fc2..a36203b 100644
--- a/core/java/android/net/LocalServerSocket.java
+++ b/core/java/android/net/LocalServerSocket.java
@@ -46,7 +46,7 @@
{
impl = new LocalSocketImpl();
- impl.create(true);
+ impl.create(LocalSocket.SOCKET_STREAM);
localAddress = new LocalSocketAddress(name);
impl.bind(localAddress);
@@ -93,7 +93,7 @@
impl.accept (acceptedImpl);
- return new LocalSocket(acceptedImpl);
+ return new LocalSocket(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
}
/**
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index 14a8094..31bc20b 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -34,21 +34,42 @@
private LocalSocketAddress localAddress;
private boolean isBound;
private boolean isConnected;
+ private final int sockType;
+
+ /** unknown socket type (used for constructor with existing file descriptor) */
+ /* package */ static final int SOCKET_UNKNOWN = 0;
+ /** Datagram socket type */
+ public static final int SOCKET_DGRAM = 1;
+ /** Stream socket type */
+ public static final int SOCKET_STREAM = 2;
+ /** Sequential packet socket type */
+ public static final int SOCKET_SEQPACKET = 3;
/**
* Creates a AF_LOCAL/UNIX domain stream socket.
*/
public LocalSocket() {
- this(new LocalSocketImpl());
+ this(SOCKET_STREAM);
+ }
+
+ /**
+ * Creates a AF_LOCAL/UNIX domain stream socket with given socket type
+ *
+ * @param sockType either {@link #SOCKET_DGRAM}, {@link #SOCKET_STREAM}
+ * or {@link #SOCKET_SEQPACKET}
+ */
+ public LocalSocket(int sockType) {
+ this(new LocalSocketImpl(), sockType);
isBound = false;
isConnected = false;
}
+
/**
* Creates a AF_LOCAL/UNIX domain stream socket with FileDescriptor.
* @hide
*/
public LocalSocket(FileDescriptor fd) throws IOException {
- this(new LocalSocketImpl(fd));
+ this(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
isBound = true;
isConnected = true;
}
@@ -57,8 +78,9 @@
* for use with AndroidServerSocket
* @param impl a SocketImpl
*/
- /*package*/ LocalSocket(LocalSocketImpl impl) {
+ /*package*/ LocalSocket(LocalSocketImpl impl, int sockType) {
this.impl = impl;
+ this.sockType = sockType;
this.isConnected = false;
this.isBound = false;
}
@@ -81,7 +103,7 @@
synchronized (this) {
if (!implCreated) {
try {
- impl.create(true);
+ impl.create(sockType);
} finally {
implCreated = true;
}
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 3b43c36..fa3cf58 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -22,6 +22,10 @@
import java.io.FileDescriptor;
import java.net.SocketOptions;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.OsConstants;
+
/**
* Socket implementation used for android.net.LocalSocket and
* android.net.LocalServerSocket. Supports only AF_LOCAL sockets.
@@ -159,7 +163,6 @@
private native int pending_native(FileDescriptor fd) throws IOException;
private native int available_native(FileDescriptor fd) throws IOException;
- private native void close_native(FileDescriptor fd) throws IOException;
private native int read_native(FileDescriptor fd) throws IOException;
private native int readba_native(byte[] b, int off, int len,
FileDescriptor fd) throws IOException;
@@ -171,8 +174,6 @@
int namespace) throws IOException;
private native void bindLocal(FileDescriptor fd, String name, int namespace)
throws IOException;
- private native FileDescriptor create_native(boolean stream)
- throws IOException;
private native void listen_native(FileDescriptor fd, int backlog)
throws IOException;
private native void shutdown(FileDescriptor fd, boolean shutdownInput);
@@ -222,15 +223,33 @@
/**
* Creates a socket in the underlying OS.
*
- * @param stream true if this should be a stream socket, false for
- * datagram.
+ * @param sockType either {@link LocalSocket#SOCKET_DGRAM}, {@link LocalSocket#SOCKET_STREAM}
+ * or {@link LocalSocket#SOCKET_SEQPACKET}
* @throws IOException
*/
- public void create (boolean stream) throws IOException {
+ public void create (int sockType) throws IOException {
// no error if socket already created
// need this for LocalServerSocket.accept()
if (fd == null) {
- fd = create_native(stream);
+ int osType;
+ switch (sockType) {
+ case LocalSocket.SOCKET_DGRAM:
+ osType = OsConstants.SOCK_DGRAM;
+ break;
+ case LocalSocket.SOCKET_STREAM:
+ osType = OsConstants.SOCK_STREAM;
+ break;
+ case LocalSocket.SOCKET_SEQPACKET:
+ osType = OsConstants.SOCK_SEQPACKET;
+ break;
+ default:
+ throw new IllegalStateException("unknown sockType");
+ }
+ try {
+ fd = Libcore.os.socket(OsConstants.AF_UNIX, osType, 0);
+ } catch (ErrnoException e) {
+ e.rethrowAsIOException();
+ }
}
}
@@ -242,7 +261,11 @@
public void close() throws IOException {
synchronized (LocalSocketImpl.this) {
if (fd == null) return;
- close_native(fd);
+ try {
+ Libcore.os.close(fd);
+ } catch (ErrnoException e) {
+ e.rethrowAsIOException();
+ }
fd = null;
}
}
diff --git a/core/java/android/net/http/HttpResponseCache.java b/core/java/android/net/http/HttpResponseCache.java
index bd50bcf..269dfb8 100644
--- a/core/java/android/net/http/HttpResponseCache.java
+++ b/core/java/android/net/http/HttpResponseCache.java
@@ -17,9 +17,9 @@
package android.net.http;
import android.content.Context;
+import com.android.okhttp.OkResponseCache;
import com.android.okhttp.ResponseSource;
import com.android.okhttp.internal.DiskLruCache;
-import com.android.okhttp.internal.http.OkResponseCache;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java
new file mode 100644
index 0000000..e52c0c3
--- /dev/null
+++ b/core/java/android/nfc/cardemulation/HostApduService.java
@@ -0,0 +1,222 @@
+package android.nfc.cardemulation;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * <p>A convenience class that can be extended to implement
+ * a service that processes ISO7816-4 commands on top of
+ * the ISO14443-4 / IsoDep protocol (T=CL).
+ *
+ * <p>To tell the platform which ISO7816 application ID (AIDs)
+ * are implemented by this service, a {@link #SERVICE_META_DATA}
+ * entry must be included in the declaration of the service. An
+ * example of such a service declaration is shown below:
+ * <pre> <service android:name=".MyHostApduService">
+ * <intent-filter>
+ * <action android:name="android.nfc.HostApduService"/>
+ * </intent-filter>
+ * <meta-data android:name="android.nfc.HostApduService" android:resource="@xml/apduservice.xml"/>
+ * </service></pre>
+ * <p>For more details refer to {@link #SERVICE_META_DATA},
+ * <code><{@link android.R.styleable#ApduService apdu-service}></code> and
+ * <code><{@link android.R.styleable#AidFilter aid-filter}></code>.
+ * <p class="note">The Android platform currently only supports a single
+ * logical channel.
+ */
+public abstract class HostApduService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.nfc.HostApduService";
+
+ /**
+ * The name of the meta-data element that contains
+ * more information about this service.
+ */
+ public static final String SERVICE_META_DATA = "android.nfc.HostApduService";
+
+ /**
+ * Reason for {@link #onDeactivated(int)}.
+ * Indicates deactivation was due to the NFC link
+ * being lost.
+ */
+ public static final int DEACTIVATION_LINK_LOSS = 0;
+
+ /**
+ * Reason for {@link #onDeactivated(int)}.
+ *
+ * <p>Indicates deactivation was due to a different AID
+ * being selected (which implicitly deselects the AID
+ * currently active on the logical channel).
+ *
+ * <p>Note that this next AID may still be resolved to this
+ * service, in which case {@link #processCommandApdu(byte[], int)}
+ * will be called again.
+ */
+ public static final int DEACTIVATION_DESELECTED = 1;
+
+ static final String TAG = "ApduService";
+
+ /**
+ * MSG_COMMAND_APDU is sent by NfcService when
+ * a 7816-4 command APDU has been received.
+ *
+ * @hide
+ */
+ public static final int MSG_COMMAND_APDU = 0;
+
+ /**
+ * MSG_RESPONSE_APDU is sent to NfcService to send
+ * a response APDU back to the remote device.
+ *
+ * @hide
+ */
+ public static final int MSG_RESPONSE_APDU = 1;
+
+ /**
+ * MSG_DEACTIVATED is sent by NfcService when
+ * the current session is finished; either because
+ * another AID was selected that resolved to
+ * another service, or because the NFC link
+ * was deactivated.
+ *
+ * @hide
+ */
+ public static final int MSG_DEACTIVATED = 2;
+
+ /**
+ * @hide
+ */
+ public static final String KEY_DATA = "data";
+
+ /**
+ * Messenger interface to NfcService for sending responses.
+ * Only accessed on main thread by the message handler.
+ */
+ Messenger mNfcService = null;
+
+ final Messenger mMessenger = new Messenger(new MsgHandler());
+
+ final class MsgHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_COMMAND_APDU:
+ Bundle dataBundle = msg.getData();
+ if (dataBundle == null) {
+ return;
+ }
+ if (mNfcService == null) mNfcService = msg.replyTo;
+
+ byte[] apdu = dataBundle.getByteArray(KEY_DATA);
+ if (apdu != null) {
+ byte[] responseApdu = processCommandApdu(apdu, 0);
+ if (responseApdu != null) {
+ if (mNfcService == null) {
+ Log.e(TAG, "Response not sent; service was deactivated.");
+ return;
+ }
+ Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU);
+ Bundle responseBundle = new Bundle();
+ responseBundle.putByteArray(KEY_DATA, responseApdu);
+ responseMsg.setData(responseBundle);
+ try {
+ mNfcService.send(responseMsg);
+ } catch (RemoteException e) {
+ Log.e("TAG", "Response not sent; RemoteException calling into " +
+ "NfcService.");
+ }
+ }
+ } else {
+ Log.e(TAG, "Received MSG_COMMAND_APDU without data.");
+ }
+ break;
+ case MSG_RESPONSE_APDU:
+ if (mNfcService == null) {
+ Log.e(TAG, "Response not sent; service was deactivated.");
+ return;
+ }
+ try {
+ mNfcService.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling into NfcService.");
+ }
+ break;
+ case MSG_DEACTIVATED:
+ // Make sure we won't call into NfcService again
+ mNfcService = null;
+ onDeactivated(msg.arg1);
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+
+ /**
+ * Sends a response APDU back to the remote device.
+ *
+ * <p>Note: this method may be called from any thread and will not block.
+ * @param responseApdu A byte-array containing the reponse APDU.
+ */
+ public final void sendResponseApdu(byte[] responseApdu) {
+ Message responseMsg = Message.obtain(null, MSG_RESPONSE_APDU);
+ Bundle dataBundle = new Bundle();
+ dataBundle.putByteArray(KEY_DATA, responseApdu);
+ responseMsg.setData(dataBundle);
+ try {
+ mMessenger.send(responseMsg);
+ } catch (RemoteException e) {
+ Log.e("TAG", "Local messenger has died.");
+ }
+ }
+
+ /**
+ * <p>This method will be called when a command APDU has been received
+ * from a remote device. A response APDU can be provided directly
+ * by returning a byte-array in this method. Note that in general
+ * response APDUs must be sent as quickly as possible, given the fact
+ * that the user is likely holding his device over an NFC reader
+ * when this method is called.
+ *
+ * <p class="note">If there are multiple services that have registered for the same
+ * AIDs in their meta-data entry, you will only get called if the user has
+ * explicitly selected your service, either as a default or just for the next tap.
+ *
+ * <p class="note">This method is running on the main thread of your application.
+ * If you cannot return a response APDU immediately, return null
+ * and use the {@link #sendResponseApdu(byte[])} method later.
+ *
+ * @param commandApdu
+ * @param flags
+ * @return a byte-array containing the response APDU, or null if no
+ * response APDU can be sent at this point.
+ */
+ public abstract byte[] processCommandApdu(byte[] commandApdu, int flags);
+
+ /**
+ * This method will be called in two possible scenarios:
+ * <li>The NFC link has been deactivated or lost
+ * <li>A different AID has been selected and was resolved to a different
+ * service component
+ * @param reason Either {@link #DEACTIVATION_LINK_LOSS} or {@link #DEACTIVATION_DESELECTED}
+ */
+ public abstract void onDeactivated(int reason);
+}
diff --git a/core/java/android/nfc/cardemulation/SeApduService.java b/core/java/android/nfc/cardemulation/SeApduService.java
new file mode 100644
index 0000000..78f8312
--- /dev/null
+++ b/core/java/android/nfc/cardemulation/SeApduService.java
@@ -0,0 +1,111 @@
+package android.nfc.cardemulation;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+
+/**
+ * <p>A convenience class that can be extended to implement
+ * a service that registers and deals with events for
+ * ISO7814-4 AIDs that reside on an embedded secure element
+ * or UICC.
+ *
+ * <p>To tell the platform which ISO7816 application ID (AIDs)
+ * are present on the Secure Element and handled by this service,
+ * a {@link #SERVICE_META_DATA} entry must be included in the declaration
+ * of the service. An example of such a service declaration is shown below:
+ * <pre> <service android:name=".MySeApduService">
+ * <intent-filter>
+ * <action android:name="android.nfc.SeApduService"/>
+ * </intent-filter>
+ * <meta-data android:name="android.nfc.SeApduService" android:resource="@xml/apduservice.xml"/>
+ * </service></pre>
+ * <p>For more details refer to {@link #SERVICE_META_DATA},
+ * <code><{@link android.R.styleable#ApduService apdu-service}></code> and
+ * <code><{@link android.R.styleable#AidFilter aid-filter}></code>.
+ */
+public abstract class SeApduService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.nfc.SeApduService";
+
+ /**
+ * The name of the meta-data element that contains
+ * more information about this service.
+ */
+ public static final String SERVICE_META_DATA = "android.nfc.SeApduService";
+
+ /**
+ * @hide
+ */
+ public static final int MSG_AID_SELECTED = 0;
+
+ /**
+ * @hide
+ */
+ public static final int MSG_HCI_TRANSACTION_EVT = 1;
+
+ /**
+ * @hide
+ */
+ public static final String KEY_AID = "aid";
+
+ /**
+ * @hide
+ */
+ public static final String KEY_PARAMETERS = "parameters";
+
+ final Messenger mMessenger = new Messenger(new MsgHandler());
+
+ final class MsgHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what){
+ case MSG_AID_SELECTED: {
+ Bundle dataBundle = msg.getData();
+ byte[] aid = dataBundle.getByteArray(KEY_AID);
+ onAidSelected(aid);
+ break;
+ }
+ case MSG_HCI_TRANSACTION_EVT: {
+ Bundle dataBundle = msg.getData();
+ byte[] aid = dataBundle.getByteArray(KEY_AID);
+ byte[] parameters = dataBundle.getByteArray(KEY_PARAMETERS);
+ onHciTransactionEvent(aid, parameters);
+ break;
+ }
+ }
+ }
+ };
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+
+ /**
+ * This method is called when an AID that has been registered
+ * in the manifest of this service has been selected on a
+ * eSE/UICC.
+ * @param aid The AID that has been selected
+ */
+ public abstract void onAidSelected(byte[] aid);
+
+ /**
+ * This method is called when a HCI transaction event has
+ * been received for an AID that has been registered
+ * in the manifest of this service.
+ * @param aid The AID of the application that generated the event
+ * @param parameters Parameters according to ETSI-TS 102 622
+ */
+ public abstract void onHciTransactionEvent(byte[] aid, byte[] parameters);
+}
\ No newline at end of file
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index b8769b4..f474504 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -16,15 +16,12 @@
package android.os;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
import java.util.Set;
/**
@@ -37,13 +34,13 @@
static {
EMPTY = new Bundle();
- EMPTY.mMap = Collections.unmodifiableMap(new HashMap<String, Object>());
+ EMPTY.mMap = ArrayMap.EMPTY;
}
// Invariant - exactly one of mMap / mParcelledData will be null
// (except inside a call to unparcel)
- /* package */ Map<String, Object> mMap = null;
+ /* package */ ArrayMap<String, Object> mMap = null;
/*
* If mParcelledData is non-null, then mMap will be null and the
@@ -65,7 +62,7 @@
* Constructs a new, empty Bundle.
*/
public Bundle() {
- mMap = new HashMap<String, Object>();
+ mMap = new ArrayMap<String, Object>();
mClassLoader = getClass().getClassLoader();
}
@@ -91,7 +88,7 @@
* inside of the Bundle.
*/
public Bundle(ClassLoader loader) {
- mMap = new HashMap<String, Object>();
+ mMap = new ArrayMap<String, Object>();
mClassLoader = loader;
}
@@ -102,7 +99,7 @@
* @param capacity the initial capacity of the Bundle
*/
public Bundle(int capacity) {
- mMap = new HashMap<String, Object>(capacity);
+ mMap = new ArrayMap<String, Object>(capacity);
mClassLoader = getClass().getClassLoader();
}
@@ -122,7 +119,7 @@
}
if (b.mMap != null) {
- mMap = new HashMap<String, Object>(b.mMap);
+ mMap = new ArrayMap<String, Object>(b.mMap);
} else {
mMap = null;
}
@@ -162,7 +159,7 @@
if (size == 0) {
return null;
}
- Object o = mMap.values().iterator().next();
+ Object o = mMap.valueAt(0);
try {
return (String) o;
} catch (ClassCastException e) {
@@ -218,9 +215,12 @@
return;
}
if (mMap == null) {
- mMap = new HashMap<String, Object>(N);
+ mMap = new ArrayMap<String, Object>(N);
+ } else {
+ mMap.erase();
+ mMap.ensureCapacity(N);
}
- mParcelledData.readMapInternal(mMap, N, mClassLoader);
+ mParcelledData.readArrayMapInternal(mMap, N, mClassLoader);
mParcelledData.recycle();
mParcelledData = null;
}
@@ -331,9 +331,8 @@
}
} else {
// It's been unparcelled, so we need to walk the map
- Iterator<Map.Entry<String, Object>> iter = mMap.entrySet().iterator();
- while (!fdFound && iter.hasNext()) {
- Object obj = iter.next().getValue();
+ for (int i=mMap.size()-1; i>=0; i--) {
+ Object obj = mMap.valueAt(i);
if (obj instanceof Parcelable) {
if ((((Parcelable)obj).describeContents()
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
@@ -1643,7 +1642,7 @@
parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L'
int oldPos = parcel.dataPosition();
- parcel.writeMapInternal(mMap);
+ parcel.writeArrayMapInternal(mMap);
int newPos = parcel.dataPosition();
// Backpatch length
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 7589a5a..bd2d9ac 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -49,4 +49,5 @@
boolean changeRestrictionsPin(in String newPin);
int checkRestrictionsPin(in String pin);
boolean hasRestrictionsPin();
+ void removeRestrictions();
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 0916ea9..12f7915 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -17,6 +17,7 @@
package android.os;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -572,7 +573,7 @@
* allows you to avoid mysterious type errors at the point of marshalling.
*/
public final void writeMap(Map val) {
- writeMapInternal((Map<String,Object>) val);
+ writeMapInternal((Map<String, Object>) val);
}
/**
@@ -593,6 +594,23 @@
}
/**
+ * Flatten an ArrayMap into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed. The Map keys must be String objects.
+ */
+ /* package */ void writeArrayMapInternal(ArrayMap<String,Object> val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ final int N = val.size();
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeValue(val.keyAt(i));
+ writeValue(val.valueAt(i));
+ }
+ }
+
+ /**
* Flatten a Bundle into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
@@ -2258,6 +2276,16 @@
}
}
+ /* package */ void readArrayMapInternal(ArrayMap outVal, int N,
+ ClassLoader loader) {
+ while (N > 0) {
+ Object key = readValue(loader);
+ Object value = readValue(loader);
+ outVal.append(key, value);
+ N--;
+ }
+ }
+
private void readListInternal(List outVal, int N,
ClassLoader loader) {
while (N > 0) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c33a28a..cdaa868 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -678,4 +678,13 @@
}
return false;
}
+
+ /** @hide */
+ public void removeRestrictions() {
+ try {
+ mService.removeRestrictions();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not change restrictions pin");
+ }
+ }
}
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index a9ee96e..ec97efb 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -973,6 +973,9 @@
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
+ if (!isResumed()) {
+ return;
+ }
super.onListItemClick(l, v, position, id);
if (mAdapter != null) {
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index caf55d7..dc683a6 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -25,11 +25,14 @@
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Settings;
import android.provider.Settings.System;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.SeekBar;
@@ -115,7 +118,7 @@
public void onActivityStop() {
if (mSeekBarVolumizer != null) {
- mSeekBarVolumizer.stopSample();
+ mSeekBarVolumizer.postStopSample();
}
}
@@ -220,10 +223,10 @@
/**
* Turns a {@link SeekBar} into a volume control.
*/
- public class SeekBarVolumizer implements OnSeekBarChangeListener, Runnable {
+ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
private Context mContext;
- private Handler mHandler = new Handler();
+ private Handler mHandler;
private AudioManager mAudioManager;
private int mStreamType;
@@ -234,6 +237,11 @@
private SeekBar mSeekBar;
private int mVolumeBeforeMute = -1;
+ private static final int MSG_SET_STREAM_VOLUME = 0;
+ private static final int MSG_START_SAMPLE = 1;
+ private static final int MSG_STOP_SAMPLE = 2;
+ private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
+
private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -255,6 +263,10 @@
mStreamType = streamType;
mSeekBar = seekBar;
+ HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
+ thread.start();
+ mHandler = new Handler(thread.getLooper(), this);
+
initSeekBar(seekBar, defaultUri);
}
@@ -285,8 +297,54 @@
}
}
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_STREAM_VOLUME:
+ mAudioManager.setStreamVolume(mStreamType, mLastProgress, 0);
+ break;
+ case MSG_START_SAMPLE:
+ onStartSample();
+ break;
+ case MSG_STOP_SAMPLE:
+ onStopSample();
+ break;
+ default:
+ Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);
+ }
+ return true;
+ }
+
+ private void postStartSample() {
+ mHandler.removeMessages(MSG_START_SAMPLE);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
+ isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0);
+ }
+
+ private void onStartSample() {
+ if (!isSamplePlaying()) {
+ onSampleStarting(this);
+ if (mRingtone != null) {
+ mRingtone.play();
+ }
+ }
+ }
+
+ private void postStopSample() {
+ // remove pending delayed start messages
+ mHandler.removeMessages(MSG_START_SAMPLE);
+ mHandler.removeMessages(MSG_STOP_SAMPLE);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_STOP_SAMPLE));
+ }
+
+ private void onStopSample() {
+ if (mRingtone != null) {
+ mRingtone.stop();
+ }
+ }
+
public void stop() {
- stopSample();
+ postStopSample();
mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
mSeekBar.setOnSeekBarChangeListener(null);
}
@@ -307,21 +365,15 @@
void postSetVolume(int progress) {
// Do the volume changing separately to give responsive UI
mLastProgress = progress;
- mHandler.removeCallbacks(this);
- mHandler.post(this);
+ mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME));
}
public void onStartTrackingTouch(SeekBar seekBar) {
}
public void onStopTrackingTouch(SeekBar seekBar) {
- if (!isSamplePlaying()) {
- startSample();
- }
- }
-
- public void run() {
- mAudioManager.setStreamVolume(mStreamType, mLastProgress, 0);
+ postStartSample();
}
public boolean isSamplePlaying() {
@@ -329,16 +381,11 @@
}
public void startSample() {
- onSampleStarting(this);
- if (mRingtone != null) {
- mRingtone.play();
- }
+ postStartSample();
}
public void stopSample() {
- if (mRingtone != null) {
- mRingtone.stop();
- }
+ postStopSample();
}
public SeekBar getSeekBar() {
@@ -347,23 +394,21 @@
public void changeVolumeBy(int amount) {
mSeekBar.incrementProgressBy(amount);
- if (!isSamplePlaying()) {
- startSample();
- }
postSetVolume(mSeekBar.getProgress());
+ postStartSample();
mVolumeBeforeMute = -1;
}
public void muteVolume() {
if (mVolumeBeforeMute != -1) {
mSeekBar.setProgress(mVolumeBeforeMute);
- startSample();
postSetVolume(mVolumeBeforeMute);
+ postStartSample();
mVolumeBeforeMute = -1;
} else {
mVolumeBeforeMute = mSeekBar.getProgress();
mSeekBar.setProgress(0);
- stopSample();
+ postStopSample();
postSetVolume(0);
}
}
diff --git a/core/java/android/print/FileDocumentAdapter.java b/core/java/android/print/FileDocumentAdapter.java
index 2871d45..c7011f4 100644
--- a/core/java/android/print/FileDocumentAdapter.java
+++ b/core/java/android/print/FileDocumentAdapter.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
import android.util.Log;
@@ -59,7 +60,8 @@
@Override
public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
- CancellationSignal cancellationSignal, LayoutResultCallback callback) {
+ CancellationSignal cancellationSignal, LayoutResultCallback callback,
+ Bundle metadata) {
// TODO: When we have a PDF rendering library we should query the page count.
PrintDocumentInfo info = new PrintDocumentInfo.Builder()
.setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN).create();
diff --git a/core/java/android/print/IPrintDocumentAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl
index 36938e3..04da157 100644
--- a/core/java/android/print/IPrintDocumentAdapter.aidl
+++ b/core/java/android/print/IPrintDocumentAdapter.aidl
@@ -16,6 +16,7 @@
package android.print;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.print.ILayoutResultCallback;
import android.print.IWriteResultCallback;
@@ -30,7 +31,7 @@
oneway interface IPrintDocumentAdapter {
void start();
void layout(in PrintAttributes oldAttributes, in PrintAttributes newAttributes,
- ILayoutResultCallback callback);
+ ILayoutResultCallback callback, in Bundle metadata);
void write(in List<PageRange> pages, in ParcelFileDescriptor fd,
IWriteResultCallback callback);
void finish();
diff --git a/core/java/android/print/PageRange.java b/core/java/android/print/PageRange.java
index 60e6229..9257a04 100644
--- a/core/java/android/print/PageRange.java
+++ b/core/java/android/print/PageRange.java
@@ -42,8 +42,10 @@
* @throws IllegalArgumentException If start is less than zero.
* @throws IllegalArgumentException If end is less than zero.
* @throws IllegalArgumentException If start greater than end.
+ *
+ * @hide
*/
- PageRange(int start, int end) {
+ public PageRange(int start, int end) {
if (start < 0) {
throw new IllegalArgumentException("start cannot be less than zero.");
}
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index 2a27a32..65f1330 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -1007,7 +1007,7 @@
*
* @return The human readable label.
*/
- public CharSequence getLabel(PackageManager packageManager) {
+ public CharSequence getLabel() {
return mLabel;
}
@@ -1203,7 +1203,7 @@
*
* @return The human readable label.
*/
- public CharSequence getLabel(PackageManager packageManager) {
+ public CharSequence getLabel() {
return mLabel;
}
diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java
index ef69400..1f83a45 100644
--- a/core/java/android/print/PrintDocumentAdapter.java
+++ b/core/java/android/print/PrintDocumentAdapter.java
@@ -16,6 +16,7 @@
package android.print;
+import android.os.Bundle;
import android.os.CancellationSignal;
import java.io.FileDescriptor;
@@ -33,14 +34,14 @@
* </li>
* <li>
* Next, you will get one or more calls to {@link #onLayout(PrintAttributes,
- * PrintAttributes, CancellationSignal, LayoutResultCallback)} to inform you
- * that the print attributes (page size, density, etc) changed giving you an
- * opportunity to layout the content to match the new constraints.
+ * PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)} to
+ * inform you that the print attributes (page size, density, etc) changed
+ * giving you an opportunity to layout the content to match the new constraints.
* </li>
* <li>
* After every call to {@link #onLayout(PrintAttributes, PrintAttributes,
- * CancellationSignal, LayoutResultCallback)}, you may get a call to {@link
- * #onWrite(List, FileDescriptor, CancellationSignal, WriteResultCallback)}
+ * CancellationSignal, LayoutResultCallback, Bundle)}, you may get a call to
+ * {@link #onWrite(List, FileDescriptor, CancellationSignal, WriteResultCallback)}
* asking you to write a PDF file with the content for specific pages.
* </li>
* <li>
@@ -49,10 +50,38 @@
* </li>
* </ul>
* </p>
+ * <h3>Implementation</h3>
+ * <p>
+ * The APIs defined in this class are designed to enable doing part or all
+ * of the work on an arbitrary thread. For example, if the printed content
+ * does not depend on the UI state, i.e. on what is shown on the screen, then
+ * you can off load the entire work on a dedicated thread, thus making your
+ * application interactive while the print work is being performed.
+ * </p>
+ * <p>
+ * You can also do work on different threads, for example if you print UI
+ * content, you can handle {@link #onStart()} and {@link #onLayout(PrintAttributes,
+ * PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)} on
+ * the UI thread (assuming onStart initializes resources needed for layout).
+ * This will ensure that the UI does not change while you are laying out the
+ * printed content. Then you can handle {@link #onWrite(List, FileDescriptor,
+ * CancellationSignal, WriteResultCallback)} and {@link #onFinish()} on another
+ * thread. This will ensure that the UI is frozen for the minimal amount of
+ * time. Also this assumes that you will generate the printed content in
+ * {@link #onLayout(PrintAttributes, PrintAttributes, CancellationSignal,
+ * LayoutResultCallback, Bundle)} which is not mandatory. If you use multiple
+ * threads, you are responsible for proper synchronization.
+ * </p>
*/
public abstract class PrintDocumentAdapter {
/**
+ * Meta-data key: mapped to a boolean value that is <code>true</code> if
+ * the current layout is for a print preview, <code>false</code> otherwise.
+ */
+ public static final String METADATA_KEY_PRINT_PREVIEW = "KEY_METADATA_PRINT_PREVIEW";
+
+ /**
* Called when printing starts. You can use this callback to allocate
* resources. This method is invoked on the main thread.
*/
@@ -65,11 +94,11 @@
* giving you a chance to layout the content such that it matches the
* new constraints. This method is invoked on the main thread.
* <p>
- * After you are done laying out, you must invoke: {@link LayoutResultCallback
- * #onLayoutFinished(PrintDocumentInfo, boolean)} with the last argument <code>true
- * </code> or <code>false</code> depending on whether the layout changed the
- * content or not, respectively; and {@link LayoutResultCallback#onLayoutFailed(
- * CharSequence), if an error occurred.
+ * After you are done laying out, you <strong>must</strong> invoke: {@link
+ * LayoutResultCallback#onLayoutFinished(PrintDocumentInfo, boolean)} with
+ * the last argument <code>true</code> or <code>false</code> depending on
+ * whether the layout changed the content or not, respectively; and {@link
+ * LayoutResultCallback#onLayoutFailed(CharSequence)}, if an error occurred.
* </p>
* <p>
* <strong>Note:</strong> If the content is large and a layout will be
@@ -84,12 +113,15 @@
* @param newAttributes The new print attributes.
* @param cancellationSignal Signal for observing cancel layout requests.
* @param callback Callback to inform the system for the layout result.
+ * @param metadata Additional information about how layout the content.
*
* @see LayoutResultCallback
* @see CancellationSignal
+ * @see #METADATA_KEY_PRINT_PREVIEW
*/
public abstract void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
- CancellationSignal cancellationSignal, LayoutResultCallback callback);
+ CancellationSignal cancellationSignal, LayoutResultCallback callback,
+ Bundle metadata);
/**
* Called when specific pages of the content should be written in the
@@ -98,7 +130,7 @@
*<p>
* After you are done writing, you should <strong>not</strong> close the
* file descriptor, rather you must invoke: {@link WriteResultCallback
- * #onWriteFinished()}, if writing completed successfully; or {@link
+ * #onWriteFinished(List)}, if writing completed successfully; or {@link
* WriteResultCallback#onWriteFailed(CharSequence)}, if an error occurred.
* </p>
* <p>
@@ -165,7 +197,7 @@
/**
* Base class for implementing a callback for the result of {@link
* PrintDocumentAdapter#onLayout(PrintAttributes, PrintAttributes,
- * CancellationSignal, LayoutResultCallback)}.
+ * CancellationSignal, LayoutResultCallback, Bundle)}.
*/
public static abstract class LayoutResultCallback {
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index 7731deb..7d42b3a 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -110,6 +110,16 @@
parcel.writeInt(mContentType);
}
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PrintDocumentInfo{");
+ builder.append("pageCount: ").append(mPageCount);
+ builder.append(", contentType: ").append(mContentType);
+ builder.append("}");
+ return builder.toString();
+ }
+
/**
* Builder for creating an {@link PrintDocumentInfo}.
*/
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 6e613bc..97384d9 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -35,11 +35,20 @@
public static final int STATE_ANY = -1;
/**
+ * Constant for matching any print job state.
+ *
+ * @hide
+ */
+ public static final int STATE_ANY_VISIBLE_TO_CLIENTS = -2;
+
+ /**
* Print job state: The print job is being created but not yet
* ready to be printed.
* <p>
* Next valid states: {@link #STATE_QUEUED}
* </p>
+ *
+ * @hide
*/
public static final int STATE_CREATED = 1;
@@ -132,6 +141,7 @@
mState = other.mState;
mAppId = other.mAppId;
mUserId = other.mUserId;
+ mTag = other.mTag;
mAttributes = other.mAttributes;
mDocumentInfo = other.mDocumentInfo;
}
@@ -143,6 +153,7 @@
mState = parcel.readInt();
mAppId = parcel.readInt();
mUserId = parcel.readInt();
+ mTag = parcel.readString();
if (parcel.readInt() == 1) {
mPageRanges = (PageRange[]) parcel.readParcelableArray(null);
}
@@ -373,6 +384,7 @@
parcel.writeInt(mState);
parcel.writeInt(mAppId);
parcel.writeInt(mUserId);
+ parcel.writeString(mTag);
if (mPageRanges != null) {
parcel.writeInt(1);
parcel.writeParcelableArray(mPageRanges, flags);
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 5ca19d4..f9f53f6 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.ICancellationSignal;
@@ -28,6 +29,7 @@
import android.os.RemoteException;
import android.print.PrintDocumentAdapter.LayoutResultCallback;
import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.os.SomeArgs;
@@ -183,6 +185,9 @@
*/
public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
PrintAttributes attributes) {
+ if (TextUtils.isEmpty(printJobName)) {
+ throw new IllegalArgumentException("priintJobName cannot be empty");
+ }
PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
mContext.getMainLooper());
try {
@@ -233,12 +238,13 @@
}
@Override
- public void layout(PrintAttributes oldAttributes,
- PrintAttributes newAttributes, ILayoutResultCallback callback) {
+ public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ ILayoutResultCallback callback, Bundle metadata) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = oldAttributes;
args.arg2 = newAttributes;
args.arg3 = callback;
+ args.arg4 = metadata;
mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget();
}
@@ -292,6 +298,7 @@
PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
PrintAttributes newAttributes = (PrintAttributes) args.arg2;
ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
+ Bundle metadata = (Bundle) args.arg4;
args.recycle();
try {
@@ -300,7 +307,7 @@
mDocumentAdapter.onLayout(oldAttributes, newAttributes,
CancellationSignal.fromTransport(remoteSignal),
- new LayoutResultCallbackWrapper(callback));
+ new LayoutResultCallbackWrapper(callback), metadata);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error printing", re);
}
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index 8a3148c..e884026 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -125,26 +125,6 @@
return builder.toString();
}
- /**
- * @hide
- */
- public String flattenToString() {
- return mServiceComponentName.flattenToString() + ":" + mLocalId;
- }
-
- /**
- * @hide
- */
- public static PrinterId unflattenFromString(String string) {
- String[] split = string.split(":");
- if (split.length != 2) {
- throw new IllegalArgumentException("Not well-formed printer id:" + string);
- }
- ComponentName component = ComponentName.unflattenFromString(split[0]);
- String localId = String.valueOf(split[1]);
- return new PrinterId(component, localId);
- }
-
public static final Parcelable.Creator<PrinterId> CREATOR =
new Creator<PrinterId>() {
@Override
diff --git a/core/java/android/print/pdf/PdfDocument.java b/core/java/android/print/pdf/PdfDocument.java
index 7ce036d..cfeb975 100644
--- a/core/java/android/print/pdf/PdfDocument.java
+++ b/core/java/android/print/pdf/PdfDocument.java
@@ -56,7 +56,7 @@
* // finish the page
* document.finishPage(page);
* . . .
- * add more pages
+ * // add more pages
* . . .
* // write the document content
* document.writeTo(getOutputStream());
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 820c2d8..dde31d2 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -245,13 +245,10 @@
* @see #removeDiscoveredPrinters(List)
* @see #onStartPrinterDiscovery()
* @see #onStopPrinterDiscovery()
- *
- * @throws IllegalStateException If this service is not connected.
*/
public final void addDiscoveredPrinters(List<PrinterInfo> printers) {
final IPrinterDiscoveryObserver observer;
synchronized (mLock) {
- throwIfNotConnectedLocked();
observer = mDiscoveryObserver;
}
if (observer != null) {
@@ -284,13 +281,10 @@
* @see #addDiscoveredPrinters(List)
* @see #onStartPrinterDiscovery()
* @see #onStopPrinterDiscovery()
- *
- * @throws IllegalStateException If this service is not connected.
*/
public final void removeDiscoveredPrinters(List<PrinterId> printerIds) {
final IPrinterDiscoveryObserver observer;
synchronized (mLock) {
- throwIfNotConnectedLocked();
observer = mDiscoveryObserver;
}
if (observer != null) {
@@ -334,13 +328,10 @@
* Gets the print jobs for the printers managed by this service.
*
* @return The print jobs.
- *
- * @throws IllegalStateException If this service is not connected.
*/
public final List<PrintJob> getPrintJobs() {
final IPrintServiceClient client;
synchronized (mLock) {
- throwIfNotConnectedLocked();
client = mClient;
}
if (client == null) {
@@ -410,12 +401,6 @@
};
}
- private void throwIfNotConnectedLocked() {
- if (mClient == null) {
- throw new IllegalStateException("Print serivice not connected");
- }
- }
-
private final class MyHandler extends Handler {
public static final int MESSAGE_START_PRINTER_DISCOVERY = 1;
public static final int MESSAGE_STOP_PRINTER_DISCOVERY = 2;
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 25af209..d743484 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -2393,7 +2393,7 @@
intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
intent.putExtra(ALARM_TIME, alarmTime);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
- manager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi);
+ manager.setExact(AlarmManager.RTC_WAKEUP, alarmTime, pi);
}
/**
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index b80a14b..22f62d70 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -61,6 +61,11 @@
private static final int CACHE_SIZE = 10;
/**
+ * @hide Special immutable empty ArrayMap.
+ */
+ public static final ArrayMap EMPTY = new ArrayMap(true);
+
+ /**
* Caches of small array objects to avoid spamming garbage. The cache
* Object[] variable is a pointer to a linked list of array objects.
* The first entry in the array is a pointer to the next array in the
@@ -71,6 +76,11 @@
static Object[] mTwiceBaseCache;
static int mTwiceBaseCacheSize;
+ /**
+ * Special hash array value that indicates the container is immutable.
+ */
+ static final int[] EMPTY_IMMUTABLE_INTS = new int[0];
+
int[] mHashes;
Object[] mArray;
int mSize;
@@ -115,6 +125,9 @@
}
private void allocArrays(final int size) {
+ if (mHashes == EMPTY_IMMUTABLE_INTS) {
+ throw new UnsupportedOperationException("ArrayMap is immutable");
+ }
if (size == (BASE_SIZE*2)) {
synchronized (ArrayMap.class) {
if (mTwiceBaseCache != null) {
@@ -204,6 +217,12 @@
mSize = 0;
}
+ private ArrayMap(boolean immutable) {
+ mHashes = EMPTY_IMMUTABLE_INTS;
+ mArray = ContainerHelpers.EMPTY_OBJECTS;
+ mSize = 0;
+ }
+
/**
* Create a new ArrayMap with the mappings from the given ArrayMap.
*/
@@ -219,7 +238,7 @@
*/
@Override
public void clear() {
- if (mSize != 0) {
+ if (mSize > 0) {
freeArrays(mHashes, mArray, mSize);
mHashes = ContainerHelpers.EMPTY_INTS;
mArray = ContainerHelpers.EMPTY_OBJECTS;
@@ -228,6 +247,20 @@
}
/**
+ * @hide
+ * Like {@link #clear}, but doesn't reduce the capacity of the ArrayMap.
+ */
+ public void erase() {
+ if (mSize > 0) {
+ final int N = mSize<<1;
+ final Object[] array = mArray;
+ for (int i=0; i<N; i++) {
+ array[i] = null;
+ }
+ }
+ }
+
+ /**
* Ensure the array map can hold at least <var>minimumCapacity</var>
* items.
*/
@@ -391,6 +424,33 @@
}
/**
+ * Special fast path for appending items to the end of the array without validation.
+ * The array must already be large enough to contain the item.
+ * @hide
+ */
+ public void append(K key, V value) {
+ int index = mSize;
+ final int hash = key.hashCode();
+ if (index >= mHashes.length) {
+ throw new IllegalStateException("Array is full");
+ }
+ if (index > 0 && mHashes[index-1] > hash) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "New hash " + hash
+ + " is before end of array hash " + mHashes[index-1]
+ + " at index " + index + " key " + key, e);
+ put(key, value);
+ return;
+ }
+ mSize = index+1;
+ mHashes[index] = hash;
+ index <<= 1;
+ mArray[index] = key;
+ mArray[index+1] = value;
+ }
+
+ /**
* Perform a {@link #put(Object, Object)} of all key/value pairs in <var>array</var>
* @param array The array whose contents are to be retrieved.
*/
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index a4cc630..bb5a6eb 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -73,7 +73,7 @@
SurfaceTexture getSurfaceTexture() {
if (mSurface == null) {
- mSurface = new SurfaceTexture(mTexture, false);
+ mSurface = new SurfaceTexture(mTexture);
}
return mSurface;
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 0289407..aea2799c 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -78,7 +78,7 @@
void addWindowToken(IBinder token, int type);
void removeWindowToken(IBinder token);
void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
- int requestedOrientation, boolean fullscreen, boolean showWhenLocked);
+ int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId);
void setAppGroupId(IBinder token, int groupId);
void setAppOrientation(IApplicationToken token, int requestedOrientation);
int getAppOrientation(IApplicationToken token);
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 2a761c1..34fa73d 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -51,6 +51,7 @@
private final int mKeyboardType;
private final KeyCharacterMap mKeyCharacterMap;
private final boolean mHasVibrator;
+ private final boolean mHasButtonUnderPad;
private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
private Vibrator mVibrator; // guarded by mMotionRanges during initialization
@@ -343,7 +344,8 @@
// Called by native code.
private InputDevice(int id, int generation, String name, String descriptor,
boolean isExternal, int sources,
- int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator) {
+ int keyboardType, KeyCharacterMap keyCharacterMap,
+ boolean hasVibrator, boolean hasButtonUnderPad) {
mId = id;
mGeneration = generation;
mName = name;
@@ -353,6 +355,7 @@
mKeyboardType = keyboardType;
mKeyCharacterMap = keyCharacterMap;
mHasVibrator = hasVibrator;
+ mHasButtonUnderPad = hasButtonUnderPad;
}
private InputDevice(Parcel in) {
@@ -365,6 +368,7 @@
mKeyboardType = in.readInt();
mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in);
mHasVibrator = in.readInt() != 0;
+ mHasButtonUnderPad = in.readInt() != 0;
for (;;) {
int axis = in.readInt();
@@ -612,6 +616,15 @@
}
/**
+ * Reports whether the device has a button under its touchpad
+ * @return Whether the device has a button under its touchpad
+ * @hide
+ */
+ public boolean hasButtonUnderPad() {
+ return mHasButtonUnderPad;
+ }
+
+ /**
* Provides information about the range of values for a particular {@link MotionEvent} axis.
*
* @see InputDevice#getMotionRange(int)
@@ -733,6 +746,7 @@
out.writeInt(mKeyboardType);
mKeyCharacterMap.writeToParcel(out, flags);
out.writeInt(mHasVibrator ? 1 : 0);
+ out.writeInt(mHasButtonUnderPad ? 1 : 0);
final int numRanges = mMotionRanges.size();
for (int i = 0; i < numRanges; i++) {
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 73fad08..5a5fc10 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1837,6 +1837,19 @@
}
}
+ /** Whether key will, by default, trigger a click on the focused view.
+ * @hide
+ */
+ public static final boolean isConfirmKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/** {@inheritDoc} */
@Override
public final int getDeviceId() {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f8aef88..188ddf2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2198,7 +2198,7 @@
* Flag indicating that the view has been through at least one layout since it
* was last attached to a window.
*/
- static final int PFLAG3_HAS_LAYOUT = 0x4;
+ static final int PFLAG3_IS_LAID_OUT = 0x4;
/**
* Flag indicating that a call to measure() was skipped and should be done
@@ -6160,8 +6160,8 @@
* Returns true if this view has been through at least one layout since it
* was last attached to or detached from a window.
*/
- public boolean hasLayout() {
- return (mPrivateFlags3 & PFLAG3_HAS_LAYOUT) == PFLAG3_HAS_LAYOUT;
+ public boolean isLaidOut() {
+ return (mPrivateFlags3 & PFLAG3_IS_LAID_OUT) == PFLAG3_IS_LAID_OUT;
}
/**
@@ -7945,21 +7945,17 @@
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean result = false;
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_ENTER: {
- if ((mViewFlags & ENABLED_MASK) == DISABLED) {
- return true;
- }
- // Long clickable items don't necessarily have to be clickable
- if (((mViewFlags & CLICKABLE) == CLICKABLE ||
- (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
- (event.getRepeatCount() == 0)) {
- setPressed(true);
- checkForLongClick(0);
- return true;
- }
- break;
+ if (KeyEvent.isConfirmKey(event.getKeyCode())) {
+ if ((mViewFlags & ENABLED_MASK) == DISABLED) {
+ return true;
+ }
+ // Long clickable items don't necessarily have to be clickable
+ if (((mViewFlags & CLICKABLE) == CLICKABLE ||
+ (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
+ (event.getRepeatCount() == 0)) {
+ setPressed(true);
+ checkForLongClick(0);
+ return true;
}
}
return result;
@@ -11813,7 +11809,7 @@
mPrivateFlags &= ~PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
}
- mPrivateFlags3 &= ~PFLAG3_HAS_LAYOUT;
+ mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
jumpDrawablesToCurrentState();
@@ -12112,7 +12108,7 @@
*/
protected void onDetachedFromWindow() {
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
- mPrivateFlags3 &= ~PFLAG3_HAS_LAYOUT;
+ mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
removeUnsetPressCallback();
removeLongPressCallback();
@@ -14439,7 +14435,7 @@
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
- mPrivateFlags3 |= PFLAG3_HAS_LAYOUT;
+ mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b0f67ac..075719c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -228,6 +228,7 @@
InputStage mFirstInputStage;
InputStage mFirstPostImeInputStage;
+ SyntheticInputStage mSyntheticInputStage;
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -589,8 +590,8 @@
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
- InputStage syntheticStage = new SyntheticInputStage();
- InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticStage);
+ mSyntheticInputStage = new SyntheticInputStage();
+ InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
@@ -3773,6 +3774,9 @@
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
+ // The synthetic stage occasionally needs to know about keys in order to debounce taps
+ mSyntheticInputStage.notifyKeyEvent(event);
+
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
@@ -3945,6 +3949,10 @@
}
super.onDeliverToNext(q);
}
+
+ public void notifyKeyEvent(KeyEvent e) {
+ mTouchNavigation.notifyKeyEvent(e);
+ }
}
/**
@@ -4375,6 +4383,9 @@
// Tap timeout in milliseconds.
private static final int TAP_TIMEOUT = 250;
+ // Debounce timeout for touch nav devices with a button under their pad, in milliseconds
+ private static final int DEBOUNCE_TIME = 250;
+
// The maximum distance traveled for a gesture to be considered a tap in millimeters.
private static final int TAP_SLOP_MILLIMETERS = 5;
@@ -4409,6 +4420,9 @@
private int mConfigTapTimeout;
private float mConfigTapSlop;
+ // Amount of time to wait between button presses and tap generation for debouncing
+ private int mConfigDebounceTime;
+
// The scaled tick distance. A movement of this amount should generally translate
// into a single dpad event in a given direction.
private float mConfigTickDistance;
@@ -4454,6 +4468,11 @@
private boolean mFlinging;
private float mFlingVelocity;
+ // The last time a confirm key was pressed on the touch nav device
+ private long mLastConfirmKeyTime = Long.MAX_VALUE;
+
+ private boolean mHasButtonUnderPad;
+
public SyntheticTouchNavigationHandler() {
super(true);
}
@@ -4497,6 +4516,8 @@
MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
mConfigMaxFlingVelocity =
MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
+ mConfigDebounceTime = DEBOUNCE_TIME;
+ mHasButtonUnderPad = device.hasButtonUnderPad();
if (LOCAL_DEBUG) {
Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId
@@ -4567,10 +4588,13 @@
if (!mConsumedMovement
&& Math.hypot(mLastX - mStartX, mLastY - mStartY) < mConfigTapSlop
&& time <= mStartTime + mConfigTapTimeout) {
- // It's a tap!
- finishKeys(time);
- sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState);
- sendKeyUp(time);
+ if (!mHasButtonUnderPad ||
+ time >= mLastConfirmKeyTime + mConfigDebounceTime) {
+ // It's a tap!
+ finishKeys(time);
+ sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState);
+ sendKeyUp(time);
+ }
} else if (mConsumedMovement
&& mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
// It might be a fling.
@@ -4603,6 +4627,13 @@
}
}
+ public void notifyKeyEvent(KeyEvent e) {
+ final int keyCode = e.getKeyCode();
+ if (KeyEvent.isConfirmKey(e.getKeyCode())) {
+ mLastConfirmKeyTime = e.getDownTime();
+ }
+ }
+
private void finishKeys(long time) {
cancelFling();
sendKeyUp(time);
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 9c00b7f..f0e6677 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -32,6 +32,7 @@
import android.media.AudioSystem;
import android.media.RingtoneManager;
import android.media.ToneGenerator;
+import android.media.VolumeController;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
@@ -55,7 +56,8 @@
*
* @hide
*/
-public class VolumePanel extends Handler implements OnSeekBarChangeListener, View.OnClickListener
+public class VolumePanel extends Handler implements OnSeekBarChangeListener, View.OnClickListener,
+ VolumeController
{
private static final String TAG = "VolumePanel";
private static boolean LOGD = false;
diff --git a/core/java/android/view/transition/TransitionManager.java b/core/java/android/view/transition/TransitionManager.java
index 7a3d9e2..7836268 100644
--- a/core/java/android/view/transition/TransitionManager.java
+++ b/core/java/android/view/transition/TransitionManager.java
@@ -320,7 +320,7 @@
* value of null causes the TransitionManager to use the default transition.
*/
public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {
- if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.hasLayout()) {
+ if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) {
if (Transition.DBG) {
Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " +
sceneRoot + ", " + transition);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 0149f03..7146d0d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -850,6 +850,23 @@
}
/**
+ * Asynchronously evaluates JavaScript in the context of the currently displayed page.
+ * If non-null, |resultCallback| will be invoked with any result returned from that
+ * execution. This method must be called on the UI thread and the callback will
+ * be made on the UI thread.
+ *
+ * @param script the JavaScript to execute.
+ * @param resultCallback A callback to be invoked when the script execution
+ * completes with the result of the execution (if any).
+ * May be null if no notificaion of the result is required.
+ * @hide pending API council approval and CTS test coverage.
+ */
+ public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
+ checkThread();
+ mProvider.evaluateJavaScript(script, resultCallback);
+ }
+
+ /**
* Saves the current view as a web archive.
*
* @param filename the filename where the archive should be placed
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 5a9e6c9..b930276 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -2656,6 +2656,12 @@
clearHelpers();
}
+ @Override
+ public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
+ // K-only API not implemented in WebViewClassic.
+ throw new IllegalStateException("This API not supported in Classic WebView.");
+ }
+
/**
* See {@link WebView#saveWebArchive(String)}
*/
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 41d6333..8c5c4ce 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -114,6 +114,8 @@
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl);
+ public void evaluateJavaScript(String script, ValueCallback<String> resultCallback);
+
public void saveWebArchive(String filename);
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 52433a57..f5c3b37 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2925,9 +2925,7 @@
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_ENTER:
+ if (KeyEvent.isConfirmKey(keyCode)) {
if (!isEnabled()) {
return true;
}
@@ -2943,7 +2941,6 @@
setPressed(false);
return true;
}
- break;
}
return super.onKeyUp(keyCode, event);
}
@@ -6500,58 +6497,67 @@
}
/**
- * Put a view into the ScrapViews list. These views are unordered.
+ * Puts a view into the list of scrap views.
+ * <p>
+ * If the list data hasn't changed or the adapter has stable IDs, views
+ * with transient state will be preserved for later retrieval.
*
* @param scrap The view to add
+ * @param position The view's position within its parent
*/
void addScrapView(View scrap, int position) {
- AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
+ final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
if (lp == null) {
return;
}
lp.scrappedFromPosition = position;
- // Don't put header or footer views or views that should be ignored
- // into the scrap heap
- int viewType = lp.viewType;
+ // Don't scrap header or footer views, or views that should
+ // otherwise not be recycled.
+ final int viewType = lp.viewType;
+ if (!shouldRecycleViewType(viewType)) {
+ return;
+ }
+
+ scrap.dispatchStartTemporaryDetach();
+
+ // Don't scrap views that have transient state.
final boolean scrapHasTransientState = scrap.hasTransientState();
- if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {
- if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && scrapHasTransientState) {
+ if (scrapHasTransientState) {
+ if (mAdapter != null && mAdapterHasStableIds) {
+ // If the adapter has stable IDs, we can reuse the view for
+ // the same data.
+ if (mTransientStateViewsById == null) {
+ mTransientStateViewsById = new LongSparseArray<View>();
+ }
+ mTransientStateViewsById.put(lp.itemId, scrap);
+ } else if (!mDataChanged) {
+ // If the data hasn't changed, we can reuse the views at
+ // their old positions.
+ if (mTransientStateViews == null) {
+ mTransientStateViews = new SparseArray<View>();
+ }
+ mTransientStateViews.put(position, scrap);
+ } else {
+ // Otherwise, we'll have to remove the view and start over.
if (mSkippedScrap == null) {
mSkippedScrap = new ArrayList<View>();
}
mSkippedScrap.add(scrap);
}
- if (scrapHasTransientState) {
- scrap.dispatchStartTemporaryDetach();
- if (mAdapter != null && mAdapterHasStableIds) {
- if (mTransientStateViewsById == null) {
- mTransientStateViewsById = new LongSparseArray<View>();
- }
- mTransientStateViewsById.put(lp.itemId, scrap);
- } else if (!mDataChanged) {
- // avoid putting views on transient state list during a data change;
- // the layout positions may be out of sync with the adapter positions
- if (mTransientStateViews == null) {
- mTransientStateViews = new SparseArray<View>();
- }
- mTransientStateViews.put(position, scrap);
- }
- }
- return;
- }
-
- scrap.dispatchStartTemporaryDetach();
- if (mViewTypeCount == 1) {
- mCurrentScrap.add(scrap);
} else {
- mScrapViews[viewType].add(scrap);
- }
+ if (mViewTypeCount == 1) {
+ mCurrentScrap.add(scrap);
+ } else {
+ mScrapViews[viewType].add(scrap);
+ }
- scrap.setAccessibilityDelegate(null);
- if (mRecyclerListener != null) {
- mRecyclerListener.onMovedToScrapHeap(scrap);
+ scrap.setAccessibilityDelegate(null);
+
+ if (mRecyclerListener != null) {
+ mRecyclerListener.onMovedToScrapHeap(scrap);
+ }
}
}
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index c4ef11c..78ba6e0 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1228,13 +1228,9 @@
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_ENTER: {
-
+ if (KeyEvent.isConfirmKey(keyCode)) {
if (mReceivedInvokeKeyDown) {
if (mItemCount > 0) {
-
dispatchPress(mSelectedChild);
postDelayed(new Runnable() {
@Override
@@ -1242,20 +1238,17 @@
dispatchUnpress();
}
}, ViewConfiguration.getPressedStateDuration());
-
+
int selectedIndex = mSelectedPosition - mFirstPosition;
performItemClick(getChildAt(selectedIndex), mSelectedPosition, mAdapter
.getItemId(mSelectedPosition));
}
}
-
+
// Clear the flag
mReceivedInvokeKeyDown = false;
-
return true;
}
- }
-
return super.onKeyUp(keyCode, event);
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index d114b76..dab0962 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -1480,7 +1480,7 @@
}
mChildToScrollTo = null;
- if (!hasLayout()) {
+ if (!isLaidOut()) {
final int scrollRange = Math.max(0,
childWidth - (r - l - mPaddingLeft - mPaddingRight));
if (mSavedState != null) {
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 3d6b69e..414c318 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -821,8 +821,7 @@
// to select one of its items
if (keyCode != KeyEvent.KEYCODE_SPACE
&& (mDropDownList.getSelectedItemPosition() >= 0
- || (keyCode != KeyEvent.KEYCODE_ENTER
- && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) {
+ || !KeyEvent.isConfirmKey(keyCode))) {
int curIndex = mDropDownList.getSelectedItemPosition();
boolean consumed;
@@ -910,16 +909,10 @@
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
boolean consumed = mDropDownList.onKeyUp(keyCode, event);
- if (consumed) {
- switch (keyCode) {
- // if the list accepts the key events and the key event
- // was a click, the text view gets the selected item
- // from the drop down as its content
- case KeyEvent.KEYCODE_ENTER:
- case KeyEvent.KEYCODE_DPAD_CENTER:
- dismiss();
- break;
- }
+ if (consumed && KeyEvent.isConfirmKey(keyCode)) {
+ // if the list accepts the key events and the key event was a click, the text view
+ // gets the selected item from the drop down as its content
+ dismiss();
}
return consumed;
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index c44ac32..2f42ae3 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1478,12 +1478,12 @@
@Override
protected void layoutChildren() {
final boolean blockLayoutRequests = mBlockLayoutRequests;
- if (!blockLayoutRequests) {
- mBlockLayoutRequests = true;
- } else {
+ if (blockLayoutRequests) {
return;
}
+ mBlockLayoutRequests = true;
+
try {
super.layoutChildren();
@@ -1495,10 +1495,10 @@
return;
}
- int childrenTop = mListPadding.top;
- int childrenBottom = mBottom - mTop - mListPadding.bottom;
+ final int childrenTop = mListPadding.top;
+ final int childrenBottom = mBottom - mTop - mListPadding.bottom;
+ final int childCount = getChildCount();
- int childCount = getChildCount();
int index = 0;
int delta = 0;
@@ -1507,8 +1507,6 @@
View oldFirst = null;
View newSel = null;
- View focusLayoutRestoreView = null;
-
AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null;
View accessibilityFocusLayoutRestoreView = null;
int accessibilityFocusPosition = INVALID_POSITION;
@@ -1570,6 +1568,7 @@
// Remember which child, if any, had accessibility focus. This must
// occur before recycling any views, since that will clear
// accessibility focus.
+ // TODO: This should rely on transient state.
final ViewRootImpl viewRootImpl = getViewRootImpl();
if (viewRootImpl != null) {
final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost();
@@ -1593,16 +1592,18 @@
}
}
+ // Ensure the child containing focus, if any, has transient state.
+ // If the list data hasn't changed, or if the adapter has stable
+ // IDs, this will maintain focus.
+ final View focusedChild = getFocusedChild();
+ if (focusedChild != null) {
+ focusedChild.setHasTransientState(true);
+ }
+
// Pull all children into the RecycleBin.
// These views will be reused if possible
final int firstPosition = mFirstPosition;
final RecycleBin recycleBin = mRecycler;
-
- // reset the focus restoration
- View focusLayoutRestoreDirectChild = null;
-
- // Don't put header or footer views into the Recycler. Those are
- // already cached in mHeaderViews;
if (dataChanged) {
for (int i = 0; i < childCount; i++) {
recycleBin.addScrapView(getChildAt(i), firstPosition+i);
@@ -1611,28 +1612,6 @@
recycleBin.fillActiveViews(childCount, firstPosition);
}
- // take focus back to us temporarily to avoid the eventual
- // call to clear focus when removing the focused child below
- // from messing things up when ViewAncestor assigns focus back
- // to someone else
- final View focusedChild = getFocusedChild();
- if (focusedChild != null) {
- // TODO: in some cases focusedChild.getParent() == null
-
- // we can remember the focused view to restore after relayout if the
- // data hasn't changed, or if the focused position is a header or footer
- if (!dataChanged || isDirectChildHeaderOrFooter(focusedChild)) {
- focusLayoutRestoreDirectChild = focusedChild;
- // remember the specific view that had focus
- focusLayoutRestoreView = findFocus();
- if (focusLayoutRestoreView != null) {
- // tell it we are going to mess with it
- focusLayoutRestoreView.onStartTemporaryDetach();
- }
- }
- requestFocus();
- }
-
// Clear out old views
detachAllViewsFromParent();
recycleBin.removeSkippedScrap();
@@ -1692,43 +1671,37 @@
recycleBin.scrapActiveViews();
if (sel != null) {
- // the current selected item should get focus if items
- // are focusable
- if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) {
- final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild &&
- focusLayoutRestoreView != null &&
- focusLayoutRestoreView.requestFocus()) || sel.requestFocus();
- if (!focusWasTaken) {
- // selected item didn't take focus, fine, but still want
- // to make sure something else outside of the selected view
- // has focus
+ final boolean shouldPlaceFocus = mItemsCanFocus && hasFocus();
+ final boolean maintainedFocus = focusedChild != null && focusedChild.hasFocus();
+ if (shouldPlaceFocus && !maintainedFocus && !sel.hasFocus()) {
+ if (sel.requestFocus()) {
+ // Successfully placed focus, clear selection.
+ sel.setSelected(false);
+ mSelectorRect.setEmpty();
+ } else {
+ // Failed to place focus, clear current (invalid) focus.
final View focused = getFocusedChild();
if (focused != null) {
focused.clearFocus();
}
positionSelector(INVALID_POSITION, sel);
- } else {
- sel.setSelected(false);
- mSelectorRect.setEmpty();
}
} else {
positionSelector(INVALID_POSITION, sel);
}
mSelectedTop = sel.getTop();
} else {
- if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {
- View child = getChildAt(mMotionPosition - mFirstPosition);
- if (child != null) positionSelector(mMotionPosition, child);
+ // If the user's finger is down, select the motion position.
+ // Otherwise, clear selection.
+ if (mTouchMode == TOUCH_MODE_TAP || mTouchMode == TOUCH_MODE_DONE_WAITING) {
+ final View child = getChildAt(mMotionPosition - mFirstPosition);
+ if (child != null) {
+ positionSelector(mMotionPosition, child);
+ }
} else {
mSelectedTop = 0;
mSelectorRect.setEmpty();
}
-
- // even if there is not selected position, we may need to restore
- // focus (i.e. something focusable in touch mode)
- if (hasFocus() && focusLayoutRestoreView != null) {
- focusLayoutRestoreView.requestFocus();
- }
}
// Attempt to restore accessibility focus.
@@ -1753,11 +1726,8 @@
}
}
- // tell focus view we are done mucking with it, if it is still in
- // our view hierarchy.
- if (focusLayoutRestoreView != null
- && focusLayoutRestoreView.getWindowToken() != null) {
- focusLayoutRestoreView.onFinishTemporaryDetach();
+ if (focusedChild != null) {
+ focusedChild.setHasTransientState(false);
}
mLayoutMode = LAYOUT_NORMAL;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 3d361f1..6680393 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -1473,7 +1473,7 @@
}
mChildToScrollTo = null;
- if (!hasLayout()) {
+ if (!isLaidOut()) {
if (mSavedState != null) {
mScrollY = mSavedState.scrollPosition;
mSavedState = null;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index a9da863..2dc822e 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -24,10 +24,11 @@
// be kept in sync with frameworks/native/include/binder/IAppOpsService.h
int checkOperation(int code, int uid, String packageName);
int noteOperation(int code, int uid, String packageName);
- int startOperation(int code, int uid, String packageName);
- void finishOperation(int code, int uid, String packageName);
+ int startOperation(IBinder token, int code, int uid, String packageName);
+ void finishOperation(IBinder token, int code, int uid, String packageName);
void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
void stopWatchingMode(IAppOpsCallback callback);
+ IBinder getToken(IBinder clientToken);
// Remaining methods are only used in Java.
List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
diff --git a/core/java/com/android/internal/app/RestrictionsPinActivity.java b/core/java/com/android/internal/app/RestrictionsPinActivity.java
index 57436f7..f8ce108 100644
--- a/core/java/com/android/internal/app/RestrictionsPinActivity.java
+++ b/core/java/com/android/internal/app/RestrictionsPinActivity.java
@@ -26,6 +26,7 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.WindowManager;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -39,17 +40,24 @@
public class RestrictionsPinActivity extends AlertActivity
implements DialogInterface.OnClickListener, TextWatcher, OnEditorActionListener {
- private UserManager mUserManager;
+ protected UserManager mUserManager;
+ protected boolean mHasRestrictionsPin;
- private EditText mPin1Text;
- private EditText mPin2Text;
- private TextView mPinErrorMessage;
- private TextView mPinMessage;
+ protected EditText mPinText;
+ protected TextView mPinErrorMessage;
+ protected TextView mPinMessage;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ mHasRestrictionsPin = mUserManager.hasRestrictionsPin();
+ initUi();
+ setupAlert();
+ }
+
+ protected void initUi() {
AlertController.AlertParams ap = mAlertParams;
ap.mTitle = getString(R.string.restr_pin_enter_pin);
ap.mPositiveButtonText = getString(R.string.ok);
@@ -58,18 +66,12 @@
ap.mNegativeButtonListener = this;
LayoutInflater inflater =
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- ap.mView = inflater.inflate(R.layout.pin_challenge, null);
+ ap.mView = inflater.inflate(R.layout.restrictions_pin_challenge, null);
mPinMessage = (TextView) ap.mView.findViewById(R.id.pin_message);
- mPin1Text = (EditText) ap.mView.findViewById(R.id.pin1_text);
- mPin2Text = (EditText) ap.mView.findViewById(R.id.pin2_text);
+ mPinText = (EditText) ap.mView.findViewById(R.id.pin_text);
mPinErrorMessage = (TextView) ap.mView.findViewById(R.id.pin_error_message);
- mPin1Text.addTextChangedListener(this);
- mPin2Text.addTextChangedListener(this);
-
- mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
-
- setupAlert();
+ mPinText.addTextChangedListener(this);
}
protected boolean verifyingPin() {
@@ -81,19 +83,12 @@
setPositiveButtonState(false);
boolean hasPin = mUserManager.hasRestrictionsPin();
- if (verifyingPin()) {
- if (hasPin) {
- mPinMessage.setVisibility(View.GONE);
- mPinErrorMessage.setVisibility(View.GONE);
- mPin2Text.setVisibility(View.GONE);
- mPin1Text.setOnEditorActionListener(this);
- updatePinTimer(-1);
- } else {
- setResult(RESULT_OK);
- finish();
- }
- } else if (hasPin) {
- // Shouldn't really be in this state, exit
+ if (hasPin) {
+ mPinMessage.setVisibility(View.GONE);
+ mPinErrorMessage.setVisibility(View.GONE);
+ mPinText.setOnEditorActionListener(this);
+ updatePinTimer(-1);
+ } else if (verifyingPin()) {
setResult(RESULT_OK);
finish();
}
@@ -114,14 +109,14 @@
seconds);
mPinErrorMessage.setText(String.format(formatString, seconds));
mPinErrorMessage.setVisibility(View.VISIBLE);
- mPin1Text.setEnabled(false);
- mPin1Text.setText("");
+ mPinText.setEnabled(false);
+ mPinText.setText("");
setPositiveButtonState(false);
- mPin1Text.postDelayed(mCountdownRunnable, Math.min(1000, pinTimerMs));
+ mPinText.postDelayed(mCountdownRunnable, Math.min(1000, pinTimerMs));
} else {
mPinErrorMessage.setVisibility(View.INVISIBLE);
- mPin1Text.setEnabled(true);
- mPin1Text.setText("");
+ mPinText.setEnabled(true);
+ mPinText.setText("");
}
}
@@ -134,20 +129,13 @@
}
}
- private void performPositiveButtonAction() {
- if (verifyingPin()) {
- int result = mUserManager.checkRestrictionsPin(mPin1Text.getText().toString());
- if (result == UserManager.PIN_VERIFICATION_SUCCESS) {
- setResult(RESULT_OK);
- finish();
- } else if (result >= 0) {
- updatePinTimer(result);
- }
- } else {
- if (mUserManager.changeRestrictionsPin(mPin1Text.getText().toString())) {
- setResult(RESULT_OK);
- finish();
- }
+ protected void performPositiveButtonAction() {
+ int result = mUserManager.checkRestrictionsPin(mPinText.getText().toString());
+ if (result == UserManager.PIN_VERIFICATION_SUCCESS) {
+ setResult(RESULT_OK);
+ finish();
+ } else if (result >= 0) {
+ updatePinTimer(result);
}
}
@@ -157,16 +145,8 @@
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- CharSequence pin1 = mPin1Text.getText();
- if (!verifyingPin()) {
- CharSequence pin2 = mPin2Text.getText();
- boolean match = pin1 != null && pin2 != null && pin1.length() >= 4
- && pin1.toString().equals(pin2.toString());
- setPositiveButtonState(match);
- mPinErrorMessage.setVisibility(match ? View.INVISIBLE : View.VISIBLE);
- } else {
- setPositiveButtonState(pin1 != null && pin1.length() >= 4);
- }
+ CharSequence pin = mPinText.getText();
+ setPositiveButtonState(pin != null && pin.length() >= 4);
}
@Override
diff --git a/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java b/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
index 35f2967..1d09292 100644
--- a/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
+++ b/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
@@ -16,13 +16,115 @@
package com.android.internal.app;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.UserManager;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
/**
* This activity is launched by Settings and other apps to either create a new PIN or
- * challenge for an existing PIN. The PIN is maintained by UserManager.
+ * change an existing PIN. The PIN is maintained by UserManager.
*/
public class RestrictionsPinSetupActivity extends RestrictionsPinActivity {
+ private EditText mNewPinText;
+ private EditText mConfirmPinText;
+
+ protected void initUi() {
+ AlertController.AlertParams ap = mAlertParams;
+ ap.mTitle = getString(R.string.restr_pin_enter_pin);
+ ap.mPositiveButtonText = getString(R.string.ok);
+ ap.mNegativeButtonText = getString(R.string.cancel);
+ ap.mPositiveButtonListener = this;
+ ap.mNegativeButtonListener = this;
+ LayoutInflater inflater =
+ (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ ap.mView = inflater.inflate(R.layout.restrictions_pin_setup, null);
+
+ mPinText = (EditText) ap.mView.findViewById(R.id.pin_text);
+ mPinMessage = (TextView) ap.mView.findViewById(R.id.pin_message);
+ mNewPinText = (EditText) ap.mView.findViewById(R.id.pin_new_text);
+ mConfirmPinText = (EditText) ap.mView.findViewById(R.id.pin_confirm_text);
+ mPinErrorMessage = (TextView) ap.mView.findViewById(R.id.pin_error_message);
+ mNewPinText.addTextChangedListener(this);
+ mConfirmPinText.addTextChangedListener(this);
+
+ if (!mHasRestrictionsPin) {
+ mPinText.setVisibility(View.GONE);
+ }
+ }
+
+ public void onResume() {
+ super.onResume();
+ setPositiveButtonState(false);
+ }
+
protected boolean verifyingPin() {
return false;
}
+
+ private void setPositiveButtonState(boolean enabled) {
+ mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(enabled);
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ setResult(RESULT_CANCELED);
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ performPositiveButtonAction();
+ } else if (which == AlertDialog.BUTTON_NEGATIVE) {
+ finish();
+ }
+ }
+
+ protected void performPositiveButtonAction() {
+ if (mHasRestrictionsPin) {
+ int result = mUserManager.checkRestrictionsPin(mPinText.getText().toString());
+ if (result != UserManager.PIN_VERIFICATION_SUCCESS) {
+ // TODO: Set message that existing pin doesn't match
+ return;
+ }
+ }
+ if (mUserManager.changeRestrictionsPin(mNewPinText.getText().toString())) {
+ // TODO: Send message to PIN recovery agent about the recovery email address
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ CharSequence pin = mPinText.getText();
+ CharSequence pin1 = mNewPinText.getText();
+ CharSequence pin2 = mConfirmPinText.getText();
+ boolean match = pin1 != null && pin2 != null && pin1.length() >= 4
+ && pin1.toString().equals(pin2.toString())
+ && (!mHasRestrictionsPin || (pin != null && pin.length() >= 4));
+ boolean showError = !TextUtils.isEmpty(pin1) && !TextUtils.isEmpty(pin2);
+ // TODO: Check recovery email address as well
+ setPositiveButtonState(match);
+ mPinErrorMessage.setVisibility((match || !showError) ? View.INVISIBLE : View.VISIBLE);
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ performPositiveButtonAction();
+ return true;
+ }
}
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index fa35308..f40f48c 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -796,16 +796,8 @@
}
throw new XmlPullParserException(
"Unexpected end of document in <string>");
- } else if (tagName.equals("int")) {
- res = Integer.parseInt(parser.getAttributeValue(null, "value"));
- } else if (tagName.equals("long")) {
- res = Long.valueOf(parser.getAttributeValue(null, "value"));
- } else if (tagName.equals("float")) {
- res = new Float(parser.getAttributeValue(null, "value"));
- } else if (tagName.equals("double")) {
- res = new Double(parser.getAttributeValue(null, "value"));
- } else if (tagName.equals("boolean")) {
- res = Boolean.valueOf(parser.getAttributeValue(null, "value"));
+ } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
+ // all work already done by readThisPrimitiveValueXml
} else if (tagName.equals("int-array")) {
parser.next();
res = readThisIntArrayXml(parser, "int-array", name);
@@ -858,6 +850,31 @@
"Unexpected end of document in <" + tagName + ">");
}
+ private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
+ throws XmlPullParserException, java.io.IOException
+ {
+ try {
+ if (tagName.equals("int")) {
+ return Integer.parseInt(parser.getAttributeValue(null, "value"));
+ } else if (tagName.equals("long")) {
+ return Long.valueOf(parser.getAttributeValue(null, "value"));
+ } else if (tagName.equals("float")) {
+ return new Float(parser.getAttributeValue(null, "value"));
+ } else if (tagName.equals("double")) {
+ return new Double(parser.getAttributeValue(null, "value"));
+ } else if (tagName.equals("boolean")) {
+ return Boolean.valueOf(parser.getAttributeValue(null, "value"));
+ } else {
+ return null;
+ }
+ } catch (NullPointerException e) {
+ throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException(
+ "Not a number in value attribute in <" + tagName + ">");
+ }
+ }
+
public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
{
int type;
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 63683b4..b03d12a 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -271,6 +271,26 @@
return true;
}
+static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jint bitmapInt,
+ int width, int height, SkBitmap::Config config, int allocSize) {
+ if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {
+ // done in native as there's no way to get BytesPerPixel in Java
+ doThrowIAE(env, "Bitmap not large enough to support new configuration");
+ return;
+ }
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapInt);
+ SkPixelRef* ref = bitmap->pixelRef();
+ SkSafeRef(ref);
+ bitmap->setConfig(config, width, height);
+ bitmap->setPixelRef(ref);
+
+ // notifyPixelsChanged will increment the generation ID even though the actual pixel data
+ // hasn't been touched. This signals the renderer that the bitmap (including width, height,
+ // and config) has changed.
+ ref->notifyPixelsChanged();
+ SkSafeUnref(ref);
+}
+
// These must match the int values in Bitmap.java
enum JavaEncodeFormat {
kJPEG_JavaEncodeFormat = 0,
@@ -666,6 +686,7 @@
(void*)Bitmap_copy },
{ "nativeDestructor", "(I)V", (void*)Bitmap_destructor },
{ "nativeRecycle", "(I)Z", (void*)Bitmap_recycle },
+ { "nativeReconfigure", "(IIIII)V", (void*)Bitmap_reconfigure },
{ "nativeCompress", "(IIILjava/io/OutputStream;[B)Z",
(void*)Bitmap_compress },
{ "nativeErase", "(II)V", (void*)Bitmap_erase },
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 3600b76..bf9177b 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -197,11 +197,10 @@
}
}
-static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jint texName,
- jobject weakThiz, jboolean allowSynchronous)
+static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jint texName, jobject weakThiz)
{
- sp<BufferQueue> bq = new BufferQueue(allowSynchronous);
- sp<GLConsumer> surfaceTexture(new GLConsumer(bq, texName));
+ sp<BufferQueue> bq = new BufferQueue();
+ sp<GLConsumer> surfaceTexture(new GLConsumer(bq, texName, GL_TEXTURE_EXTERNAL_OES, true, true));
if (surfaceTexture == 0) {
jniThrowException(env, OutOfResourcesException,
"Unable to create native SurfaceTexture");
@@ -286,7 +285,7 @@
static JNINativeMethod gSurfaceTextureMethods[] = {
{"nativeClassInit", "()V", (void*)SurfaceTexture_classInit },
- {"nativeInit", "(ILjava/lang/Object;Z)V", (void*)SurfaceTexture_init },
+ {"nativeInit", "(ILjava/lang/Object;)V", (void*)SurfaceTexture_init },
{"nativeFinalize", "()V", (void*)SurfaceTexture_finalize },
{"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
{"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage },
diff --git a/core/jni/android_hardware_photography_CameraMetadata.cpp b/core/jni/android_hardware_photography_CameraMetadata.cpp
index 5070d2c..5190a37 100644
--- a/core/jni/android_hardware_photography_CameraMetadata.cpp
+++ b/core/jni/android_hardware_photography_CameraMetadata.cpp
@@ -267,7 +267,13 @@
if (src == NULL) {
// If array is NULL, delete the entry
- res = metadata->erase(tag);
+ if (metadata->exists(tag)) {
+ res = metadata->erase(tag);
+ ALOGV("%s: Erase values (res = %d)", __FUNCTION__, res);
+ } else {
+ res = OK;
+ ALOGV("%s: Don't need to erase", __FUNCTION__);
+ }
} else {
// Copy from java array into native array
ScopedByteArrayRO arrayReader(env, src);
@@ -275,6 +281,8 @@
res = Helpers::updateAny(metadata, static_cast<uint32_t>(tag),
tagType, arrayReader.get(), arrayReader.size());
+
+ ALOGV("%s: Update values (res = %d)", __FUNCTION__, res);
}
if (res == OK) {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 67c2cfd..139b7be 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -117,10 +117,36 @@
return env->NewStringUTF(AudioSystem::getParameters(0, c_keys8).string());
}
+static JNIEnv* AudioSystem_getJNIEnv(bool* needsDetach) {
+ *needsDetach = false;
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ int result = vm->AttachCurrentThread(&env, (void*) &args);
+ if (result != JNI_OK) {
+ ALOGE("thread attach failed: %#x", result);
+ return NULL;
+ }
+ *needsDetach = true;
+ }
+ return env;
+}
+
+static void AudioSystem_detachJNI() {
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ int result = vm->DetachCurrentThread();
+ if (result != JNI_OK) {
+ ALOGE("thread detach failed: %#x", result);
+ }
+}
+
static void
android_media_AudioSystem_error_callback(status_t err)
{
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ bool needsDetach = false;
+ JNIEnv *env = AudioSystem_getJNIEnv(&needsDetach);
+
if (env == NULL) {
return;
}
@@ -142,6 +168,10 @@
}
env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz, "errorCallbackFromNative","(I)V"), error);
+
+ if (needsDetach) {
+ AudioSystem_detachJNI();
+ }
}
static int
@@ -282,6 +312,12 @@
return (jint) afLatency;
}
+static jint
+android_media_AudioSystem_setLowRamDevice(JNIEnv *env, jobject clazz, jboolean isLowRamDevice)
+{
+ return (jint) AudioSystem::setLowRamDevice((bool) isLowRamDevice);
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
@@ -308,6 +344,7 @@
{"getPrimaryOutputSamplingRate", "()I", (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
{"getPrimaryOutputFrameCount", "()I", (void *)android_media_AudioSystem_getPrimaryOutputFrameCount},
{"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency},
+ {"setLowRamDevice", "(Z)I", (void *)android_media_AudioSystem_setLowRamDevice},
};
int register_android_media_AudioSystem(JNIEnv *env)
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index f2b69c6..b9ed28e 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -44,26 +44,6 @@
static jclass class_FileDescriptor;
static jmethodID method_CredentialsInit;
-/*
- * private native FileDescriptor
- * create_native(boolean stream)
- * throws IOException;
- */
-static jobject
-socket_create (JNIEnv *env, jobject object, jboolean stream)
-{
- int ret;
-
- ret = socket(PF_LOCAL, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
-
- if (ret < 0) {
- jniThrowIOException(env, errno);
- return NULL;
- }
-
- return jniCreateFileDescriptor(env,ret);
-}
-
/* private native void connectLocal(FileDescriptor fd,
* String name, int namespace) throws IOException
*/
@@ -445,32 +425,6 @@
#endif
}
-static void socket_close (JNIEnv *env, jobject object, jobject fileDescriptor)
-{
- int fd;
- int err;
-
- if (fileDescriptor == NULL) {
- jniThrowNullPointerException(env, NULL);
- return;
- }
-
- fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-
- if (env->ExceptionOccurred() != NULL) {
- return;
- }
-
- do {
- err = close(fd);
- } while (err < 0 && errno == EINTR);
-
- if (err < 0) {
- jniThrowIOException(env, errno);
- return;
- }
-}
-
/**
* Processes ancillary data, handling only
* SCM_RIGHTS. Creates appropriate objects and sets appropriate
@@ -909,7 +863,6 @@
/* name, signature, funcPtr */
{"getOption_native", "(Ljava/io/FileDescriptor;I)I", (void*)socket_getOption},
{"setOption_native", "(Ljava/io/FileDescriptor;III)V", (void*)socket_setOption},
- {"create_native", "(Z)Ljava/io/FileDescriptor;", (void*)socket_create},
{"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
(void*)socket_connect_local},
{"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local},
@@ -918,7 +871,6 @@
{"shutdown", "(Ljava/io/FileDescriptor;Z)V", (void*)socket_shutdown},
{"available_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_available},
{"pending_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_pending},
- {"close_native", "(Ljava/io/FileDescriptor;)V", (void*) socket_close},
{"read_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_read},
{"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba},
{"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba},
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 8ef5d0b..e5e3db0 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -57,7 +57,7 @@
gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(),
nameObj.get(), descriptorObj.get(), deviceInfo.isExternal(),
deviceInfo.getSources(), deviceInfo.getKeyboardType(),
- kcmObj.get(), deviceInfo.hasVibrator()));
+ kcmObj.get(), deviceInfo.hasVibrator(), deviceInfo.hasButtonUnderPad()));
const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
for (size_t i = 0; i < ranges.size(); i++) {
@@ -86,8 +86,8 @@
FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz));
- GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz,
- "<init>", "(IILjava/lang/String;Ljava/lang/String;ZIILandroid/view/KeyCharacterMap;Z)V");
+ GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz, "<init>",
+ "(IILjava/lang/String;Ljava/lang/String;ZIILandroid/view/KeyCharacterMap;ZZ)V");
GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
"addMotionRange", "(IIFFFFF)V");
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 842a7f7..bf6753d 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -107,7 +107,7 @@
return NULL;
}
- sp<Surface> surface(new Surface(bufferProducer));
+ sp<Surface> surface(new Surface(bufferProducer, true));
if (surface == NULL) {
return NULL;
}
@@ -143,7 +143,7 @@
}
sp<IGraphicBufferProducer> bq = st->getBufferQueue();
- sp<Surface> surface(new Surface(bq));
+ sp<Surface> surface(new Surface(bq, true));
if (surface == NULL) {
jniThrowException(env, OutOfResourcesException, NULL);
return 0;
@@ -319,7 +319,7 @@
sp<IGraphicBufferProducer> gbp(interface_cast<IGraphicBufferProducer>(binder));
if (gbp != NULL) {
// we have a new IGraphicBufferProducer, create a new Surface for it
- sur = new Surface(gbp);
+ sur = new Surface(gbp, true);
// and keep a reference before passing to java
sur->incStrong(&sRefBaseOwner);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bfd7560..fb8359b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2513,6 +2513,12 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.updates.CarrierProvisioningUrlsInstallReceiver" >
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS" />
+ </intent-filter>
+ </receiver>
+
<receiver android:name="com.android.server.updates.TZInfoInstallReceiver" >
<intent-filter>
<action android:name="android.intent.action.UPDATE_TZINFO" />
diff --git a/core/res/res/layout/pin_challenge.xml b/core/res/res/layout/restrictions_pin_challenge.xml
similarity index 65%
copy from core/res/res/layout/pin_challenge.xml
copy to core/res/res/layout/restrictions_pin_challenge.xml
index 2cb14b4..954af92 100644
--- a/core/res/res/layout/pin_challenge.xml
+++ b/core/res/res/layout/restrictions_pin_challenge.xml
@@ -37,38 +37,13 @@
android:text="@string/restr_pin_create_pin"
android:textColor="?android:attr/textColorSecondary" />
- <!-- TextView android:id="@+id/pin1_label"
- style="?android:attr/textAppearanceSmall"
- android:layout_marginBottom="16dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/restr_pin_enter_pin"
- android:textColor="?android:attr/textColorSecondary" /-->
-
- <EditText android:id="@+id/pin1_text"
+ <EditText android:id="@+id/pin_text"
style="?android:attr/textAppearanceMedium"
android:layout_marginBottom="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/restr_pin_enter_pin"
- android:inputType="textPassword"
- android:textColor="?android:attr/textColorPrimary" />
-
- <!-- TextView android:id="@+id/pin2_label"
- style="?android:attr/textAppearanceSmall"
- android:layout_marginBottom="16dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/restr_pin_confirm_pin"
- android:textColor="?android:attr/textColorSecondary" /-->
-
- <EditText android:id="@+id/pin2_text"
- style="?android:attr/textAppearanceMedium"
- android:layout_marginBottom="16dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/restr_pin_confirm_pin"
- android:inputType="textPassword"
+ android:inputType="numberPassword"
android:textColor="?android:attr/textColorPrimary" />
<TextView android:id="@+id/pin_error_message"
diff --git a/core/res/res/layout/pin_challenge.xml b/core/res/res/layout/restrictions_pin_setup.xml
similarity index 76%
rename from core/res/res/layout/pin_challenge.xml
rename to core/res/res/layout/restrictions_pin_setup.xml
index 2cb14b4..03ed696 100644
--- a/core/res/res/layout/pin_challenge.xml
+++ b/core/res/res/layout/restrictions_pin_setup.xml
@@ -37,38 +37,31 @@
android:text="@string/restr_pin_create_pin"
android:textColor="?android:attr/textColorSecondary" />
- <!-- TextView android:id="@+id/pin1_label"
- style="?android:attr/textAppearanceSmall"
- android:layout_marginBottom="16dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/restr_pin_enter_pin"
- android:textColor="?android:attr/textColorSecondary" /-->
-
- <EditText android:id="@+id/pin1_text"
+ <EditText android:id="@+id/pin_text"
style="?android:attr/textAppearanceMedium"
android:layout_marginBottom="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:hint="@string/restr_pin_enter_pin"
- android:inputType="textPassword"
+ android:hint="@string/restr_pin_enter_old_pin"
+ android:inputType="numberPassword"
android:textColor="?android:attr/textColorPrimary" />
- <!-- TextView android:id="@+id/pin2_label"
- style="?android:attr/textAppearanceSmall"
+ <EditText android:id="@+id/pin_new_text"
+ style="?android:attr/textAppearanceMedium"
android:layout_marginBottom="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/restr_pin_confirm_pin"
- android:textColor="?android:attr/textColorSecondary" /-->
+ android:hint="@string/restr_pin_enter_new_pin"
+ android:inputType="numberPassword"
+ android:textColor="?android:attr/textColorPrimary" />
- <EditText android:id="@+id/pin2_text"
+ <EditText android:id="@+id/pin_confirm_text"
style="?android:attr/textAppearanceMedium"
android:layout_marginBottom="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/restr_pin_confirm_pin"
- android:inputType="textPassword"
+ android:inputType="numberPassword"
android:textColor="?android:attr/textColorPrimary" />
<TextView android:id="@+id/pin_error_message"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 0d206d3..3d6d9da 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Laat die program toe om SurfaceFlinger se laevlak-kenmerke te gebruik."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lees raambuffer"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Laat die program toe om die inhoud van die raambuffer te lees."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"stel Wi-Fi-skerms op"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Laat die program toe om Wi-Fi-skerms op te stel en daaraan te koppel."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"beheer Wi-Fi-skerms"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Laat die program toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander programme geplaas is."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bind aan \'n kennisgewingluisteraardiens"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Laat die houer toe om aan die top-koppelvlak van \'n kennisgewingluisteraardiens te bind. Behoort nooit vir gewone programme nodig te wees nie."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Stel wagwoordreëls"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Beheer lengte en watter karakters wat in die skermontsluit-wagwoorde gebruik word."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitor pogings om skerm te ontsluit"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index be512f6..e719f72 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"መተግበሪያውን የSurfaceFlinger ዝቅተኛ ደረጃ ባህሪያትን ለመጠቀም ይፈቅዳል።"</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"የንዑስ ክፈፍ ቋት አንብብ"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"የክፈፍ ቋት ይዘት ለማንበብ ለመተግበሪያው ይፈቅዳሉ።"</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"የWifi ማሳያዎችን አዋቅር"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"መተግበሪያው የWifi ማሳያዎችን እንዲያዋቅርና ከእነሱ ጋር እንዲገናኝ ይፈቅድለታል።"</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"የWifi ማሳያዎችን ተቆጣጠር"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"መተግበሪያው ማሳወቂያዎችን እንዲያስመጣ፣ እንዲመረምር እና እንዲያጸዳ ያስችለዋል፣ በሌሎች መተግበሪያዎች የተለጠፉትንም ጨምሮ።"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ከአንድ የማሳወቂያ አዳማጭ አገልግሎት ጋር ይሰሩ"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"ያዢው የማሳወቂያ አዳማጭ አገልግሎቱን ከከፍተኛ-ደረጃ በይነገጹ ጋር እንዲያስር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ አያስፈልግም።"</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"የይለፍ ቃል ድንቦች አዘጋጅ"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"በማያ-መክፈት የተፈቀዱ የይለፍ ቃል ርዝመት እና ቁምፊዎች ተቆጣጠር።"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"የማሳያ-ክፈት ሙከራዎችን አሳይ"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 39320d7..88c55c5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"للسماح للتطبيق باستخدام ميزات SurfaceFlinger ذات المستوى المنخفض."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"قراءة المخزن المؤقت للإطارات"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"للسماح للتطبيق بقراءة محتوى المخزن المؤقت للإطارات."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"تهيئة شاشات Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"للسماح للتطبيق بتهيئة شاشات Wi-Fi والاتصال بها."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"التحكم في شاشات Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"يتيح للتطبيق استرجاع الإشعارات وفحصها ومسحها، بما في ذلك تلك التي نشرتها تطبيقات أخرى."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"الربط بخدمة تلقّي الإشعارات الصوتية"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"يتيح للمالك الربط بواجهة المستوى العلوي لخدمة تلقّي الإشعارات الصوتية. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"تعيين قواعد كلمة المرور"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"يمكنك التحكم في الطول والأحرف المسموح بها في كلمات مرور إلغاء تأمين الشاشة."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"مراقبة محاولات إلغاء قفل الشاشة"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index e27ba07..d6997f0 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Дазваляе прыкладанням выкарыстоўваць нізкаўзроўневыя функцыі SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"чытаць буфер кадраў"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Дазваляе прыкладанням счытваць змесціва буферу кадра."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"налада дысплеяў Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Дазволiць прыкладанню наладжвацца i падключацца да дысплеяў Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"кіраванне дысплеямi Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Дазваляе прыкладанню атрымлiваць, правяраць i выдаляць апавяшчэннi, у тым лiку апублiкаваныя iншымi прыкладаннямi."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"прывязка да службы апавяшчэння слухача"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дазваляе ўладальніку прывязвацца да верхняга ўзроўню інтэрфейсу службы апавяшчэння слухачоў. Ніколі не патрэбнае для звычайных прыкладанняў."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Устанавіць правілы паролю"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Кіраванне даўжынёй і колькасцю знакаў у паролі разблакоўкі экрана."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Сачыць за спробамі разблакоўкі экрана"</string>
@@ -1598,6 +1606,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 2a1c01d..8d0826c 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Разрешава на приложението да използва функциите на SurfaceFlinger от ниско ниво."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"четене на кадровия буфер"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Разрешава на приложението да чете съдържанието на кадровия буфер."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"конфигуриране на дисплеите през WiFi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Разрешава на приложението да конфигурира и да се свързва с дисплеите през WiFi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"контролиране на дисплеите през WiFi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Разрешава на приложението да извлича, преглежда и изчиства известия, включително публикуваните от други приложения."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"обвързване с услуга за слушател на известия"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Разрешава на притежателя да се обвърже с интерфейса от първо ниво на услуга за слушател на известия. Нормалните приложения не би трябвало никога да се нуждаят от това."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Задаване на правила за паролата"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролирайте дължината и разрешените знаци за паролите за отключване на екрана."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Наблюдаване на опитите за отключване на екрана"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 8348e0f..bc7b539 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permet que l\'aplicació utilitzi funcions SurfaceFlinger de baix nivell."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"llegir la memòria intermèdia de marcs"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permet que l\'aplicació llegeixi el contingut de la memòria intermèdia de marcs."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configuració de les pantalles Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permet a l\'aplicació configurar-se i connectar-se a les pantalles Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"control de les pantalles Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet que l\'aplicació recuperi, examini i esborri les notificacions, incloses les que han publicat altres aplicacions."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincula a un servei de processament de notificacions"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permet que el titular vinculi la interfície de nivell superior d\'un servei de processament de notificacions. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Defineix les normes de contrasenya"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controla la longitud i els caràcters permesos a les contrasenyes de desbloqueig de pantalla."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Control d\'intents de desbloqueig de pantalla"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 836715e..7805977 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Umožňuje aplikaci používat nízkoúrovňové funkce SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"čtení vyrovnávací paměti snímků"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Umožňuje aplikaci číst obsah vyrovnávací paměti snímků."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurovat displeje přes Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Povoluje aplikaci připojit a konfigurovat displeje přes Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"ovládat displeje přes Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikacím načítat, zobrazovat a mazat oznámení včetně těch přidaných jinými aplikacemi."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"navázání na službu pro poslouchání oznámení"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Umožňuje držiteli navázat se na nejvyšší úroveň služby pro poslouchání oznámení. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Nastavit pravidla pro heslo"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Řídit délku hesel pro odemčení obrazovky a povolené znaky."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Sledovat pokusy o odemčení obrazovky"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9964272..5bb2ae8 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Tillader, at appen kan bruge SurfaceFlinger-funktioner på lavt niveau."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"læs rammebuffer"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Tillader, at appen kan læse indholdet fra rammebufferen."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurer Wi-Fi-skærme"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Tillader, at appen konfigurerer og opretter forbindelse til Wi-Fi-skærme."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kontrollér Wi-Fi-skærme"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillader, at appen kan hente, undersøge og rydde underretninger, herunder dem, der er sendt af andre apps."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"forpligte sig til en underretningslyttertjeneste"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Tillader brugeren at forpligte sig til en underretningslyttertjenestes grænseflade på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Indstil regler for adgangskode"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontroller længden samt tilladte tegn i adgangskoder til oplåsning af skærmen."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Overvåg forsøg på oplåsning af skærm"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index fad1558..7716e84 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Ermöglicht der App, die systemnahen SurfaceFlinger-Funktionen zu verwenden"</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Frame-Puffer lesen"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Ermöglicht der App, den Inhalt des Frame-Puffers zu lesen"</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"WLAN-Anzeigen konfigurieren"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Erlaubt der App, WLAN-Anzeigen zu konfigurieren und eine Verbindung zu diesen herzustellen"</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"WLAN-Anzeigen steuern"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ermöglicht der App das Abrufen, Überprüfen und Löschen von Benachrichtigungen, einschließlich Benachrichtigungen, die von anderen Apps gepostet wurden"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"An Benachrichtigungs-Listener-Dienst binden"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ermöglicht dem Inhaber, sich an die Oberfläche der obersten Ebene eines Benachrichtigungs-Listener-Dienstes zu binden. Sollte nie für normale Apps benötigt werden."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Passwortregeln festlegen"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Zulässige Länge und Zeichen für Passwörter zum Entsperren des Bildschirms festlegen"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Versuche zum Entsperren des Displays überwachen"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 07df59f..fecbaaa 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Επιτρέπει σε μια εφαρμογή να χρησιμοποιεί λειτουργίες SurfaceFlinger χαμηλού επιπέδου."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"ανάγνωση προσωρινής μνήμης πλαισίου"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Επιτρέπει στην εφαρμογή την ανάγνωση του περιεχομένου της προσωρινής μνήμης πλαισίου."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"διαμόρφωση οθονών Wifi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Επιτρέπει τη διαμόρφωση της εφαρμογής και τη σύνδεσης σε οθόνες Wifi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"έλεγχος οθονών Wifi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Επιτρέπει στην εφαρμογή να ανακτά, να εξετάζει και να απαλείφει ειδοποιήσεις, συμπεριλαμβανομένων εκείνων που δημοσιεύονται από άλλες εφαρμογές."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"δέσμευση σε υπηρεσία ακρόασης ειδοποίησης"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας ακρόασης ειδοποιήσεων. Δεν απαιτείται σε κανονικές εφαρμογές."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Ορισμός κανόνων κωδικού πρόσβασης"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Έλεγχος του μεγέθους και των χαρακτήρων που επιτρέπονται στους κωδικούς πρόσβασης ξεκλειδώματος οθόνης."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Παρακολούθηση προσπαθειών ξεκλειδώματος οθόνης"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ac1ef54..63d5f66 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Allows the app to use SurfaceFlinger low-level features."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"read frame buffer"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Allows the app to read the content of the frame buffer."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configure Wi-Fi displays"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Allows the app to configure and connect to Wi-Fi displays."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"control Wi-Fi displays"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Allows the app to retrieve, examine, and clear notifications, including those posted by other apps."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bind to a notification listener service"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Set password rules"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Control the length and the characters allowed in screen-unlock passwords."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitor screen-unlock attempts"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 4a4dfd0..1eafed5 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite que la aplicación utilice funciones de SurfaceFlinger de bajo nivel."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"leer el búfer de tramas"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite que la aplicación lea el contenido del búfer de tramas."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurar pantallas Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite que la aplicación configure y se conecte a pantallas Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlar pantallas Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y elimine notificaciones, incluidas aquellas publicadas por otras aplicaciones."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"Vincular a un servicio de agente de escucha de notificaciones"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite al propietario vincularse a la interfaz de nivel superior de un servicio de agente de escucha de notificaciones. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Establecer reglas de contraseña"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas para desbloquear la pantalla"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Supervisa los intentos para desbloquear la pantalla"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 5069333..dd4bac9 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite que la aplicación use funciones de SurfaceFlinger de nivel inferior."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"leer memoria de almacenamiento intermedio"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite que la aplicación lea el contenido de la memoria de almacenamiento intermedio."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurar pantallas Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite que la aplicación configure pantallas Wi-Fi y se conecte a ellas."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlar pantallas Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y borre notificaciones, incluidas las que han publicado otras aplicaciones."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"enlazar con un servicio de detector de notificaciones"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite enlazar con la interfaz de nivel superior de un servicio de detector de notificaciones. No debe ser necesario para las aplicaciones normales."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Establecimiento de reglas de contraseña"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas de bloqueo de pantalla"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Control de intentos de bloqueo de pantalla"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 1a2bc60..d5a8eed 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Võimaldab rakendusel kasutada SurfaceFlingeri madalatasemelisi funktsioone."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"loe kaadripuhvrit"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Võimaldab rakendusel kaadripuhvri sisu lugeda."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"WiFi-ekraanide seadistamine"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Lubab rakendusel seadistada WiFi-ekraane ja nendega ühendus luua."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"WiFi-ekraanide juhtimine"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Võimaldab rakendusel tuua, kontrollida ja kustutada märguandeid, sh neid, mille on postitanud teised rakendused."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"seo märguannete kuulamisteenusega"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Võimaldab omanikul siduda märguannete kuulamisteenuse ülemise taseme kasutajaliidese. Seda ei tohiks tavarakenduste puhul kunagi vaja olla."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Parooli reeglite määramine"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrollige ekraaniluku avamise paroolide pikkust ja tähemärke."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Ekraani avamiskatsed"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 697450d..6043d64 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"اجازه میدهد برنامه از ویژگیهای سطح پایین SurfaceFlinger استفاده کند."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"خواندن بافر قاب"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"به برنامه اجازه میدهد تا محتوای بافر کادر را بخواند."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"پیکربندی صفحه نمایشهای Wi‑Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"به برنامه اجازه میدهد تا اتصال به صفحات نمایش Wi‑Fi را پیکربندی کند."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"کنترل صفحه نمایشهای Wi‑Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"به برنامه اجازه میدهد به بازیابی، بررسی و پاک کردن اعلانها از جمله موارد پست شده توسط سایر برنامهها بپردازد."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"اتصال به یک سرویس شنونده اعلان"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"به دارنده اجازه میدهد به یک رابط سطح بالای سرویس شنونده اعلان متصل شود. هرگز نباید برای برنامههای عادی لازم شود."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"طول و نویسههای مجاز در گذرواژههای بازکردن قفل صفحه را کنترل کنید."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاشهای قفل گشایی صفحه"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 34c5eff..744d818 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Antaa sovelluksen käyttää SurfaceFlingerin matalan tason ominaisuuksia."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lue kehyspuskuria"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Antaa sovelluksen lukea kehyspuskurin sisältöä."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"määritä wifi-näyttöjen asetukset"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Antaa sovelluksen määrittää wifi-näyttöjä ja muodostaa yhteyden niihin."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"hallitse wifi-näyttöjä"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Antaa sovelluksen noutaa, tutkia ja tyhjentää ilmoituksia (myös muiden sovelluksien lähettämiä)."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"sido ilmoituskuuntelijapalveluun"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Antaa sovelluksen sitoutua ilmoituskuuntelijan ylimmän tason käyttöliittymään. Ei tavallisten sovelluksien käyttöön."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Aseta salasanasäännöt"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Hallinnoi ruudun lukituksenpoistosalasanoissa sallittuja merkkejä ja salasanan pituutta."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Tarkkaile ruudun lukituksen poistoyrityksiä"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 6245112..8e75805 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permet à l\'application d\'utiliser les fonctionnalités de bas niveau de SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Lecture de la mémoire tampon graphique"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permet à l\'application de lire le contenu de la mémoire tampon graphique."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurer les écrans Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permet à l\'application de configurer des écrans Wi-Fi et de s\'y connecter."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"contrôler les écrans Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet aux applications de récupérer, d\'examiner et d\'autoriser les notifications, y compris celles envoyées par d\'autres applications."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permet à l\'application de s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications. Ne devrait jamais être nécessaire pour les applications normales."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Définir les règles du mot de passe"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Choisir le nombre et le type de caractères autorisés dans les mots de passe de déverrouillage de l\'écran"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Gérer les tentatives de déverrouillage de l\'écran"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index c55cc2f..8c63d92 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"एप्लिकेशन को SurfaceFlinger निम्न-स्तर सुविधाएं उपयोग करने देता है."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"फ़्रेम बफ़र पढ़ें"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"एप्लिकेशन को फ़्रेम बफ़र की सामग्री पढ़ने देता है."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wifi डिस्प्ले को कॉन्फ़िगर करें"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"एप्लिकेशन को कॉन्फ़िगर करने देता है और Wifi डिस्प्ले से कनेक्ट करता है."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wifi डिस्प्ले को नियंत्रित करें"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"एप्लिकेशन को सूचनाओं को प्राप्त करने, जांच करने, और साफ़ करने देता है, जिनमें अन्य एप्लिकेशन के द्वारा पोस्ट की गई सूचनाएं भी शामिल हैं."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"सूचना श्रवणकर्ता सेवा से जुड़ें"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"धारक को सूचना श्रवणकर्ता सेवा के शीर्ष स्तरीय इंटरफ़ेस से जुड़ने देती है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होनी चाहिए."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"पासवर्ड नियम सेट करें"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"स्क्रीन-अनलॉक पासवर्ड में अनुमति प्राप्त लंबाई और वर्णों को नियंत्रित करें."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"स्क्रीन-अनलॉक के प्रयासों पर निगरानी रखें"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index a73f79f..0331bd8 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Aplikaciji omogućuje upotrebu značajki niske razine SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"čitanje međuspremnika okvira"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Aplikaciji omogućuje čitanje sadržaja međuspremnika okvira."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfiguriraj Wifi zaslone"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Omogućuje aplikaciji konfiguriranje i povezivanje s Wi-Fi zaslonima."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"upravljaj Wifi zaslonima"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Omogućuje aplikaciji dohvaćanje, pregledavanje i brisanje obavijesti, uključujući obavijesti drugih aplikacija."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vezanje uz uslugu slušatelja obavijesti"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Nositelju omogućuje vezanje uz sučelje najviše razine usluge slušatelja obavijesti. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Postavi pravila zaporke"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Upravljajte duljinom zaporki za otključavanje zaslona i dopuštenim znakovima u tim zaporkama."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Nadgledaj pokušaje otključavanja zaslona"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 42ea215..0e3359b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Lehetővé teszi az alkalmazás számára a SurfaceFlinger alacsony szintű funkciók használatát."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"keretpuffer olvasása"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Lehetővé teszi az alkalmazás számára a keretpuffer tartalmának olvasását."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wi-Fi kijelzők konfigurálása"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Lehetővé teszi, hogy az alkalmazás Wi-Fi kijelzőket konfiguráljon, és csatlakozzon hozzájuk."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wi-Fi kijelzők irányítása"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Lehetővé teszi, hogy az alkalmazás értesítéseket kérdezzen le, vizsgáljon és tisztítson meg, beleértve az egyéb alkalmazások által közzétett értesítéseket is."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"csatlakozzon értesítésfigyelő szolgáltatáshoz"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lehetővé teszi a használó számára, hogy csatlakozzon egy értesítésfigyelő szolgáltatás legfelső szintű felületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Jelszavakkal kapcsolatos szabályok beállítása"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"A képernyőzár-feloldási jelszavakban engedélyezett karakterek és hosszúság vezérlése."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Képernyőzár-feloldási kísérletek figyelése"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 9f8881b..c01df57 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Mengizinkan apl menggunakan fitur tingkat rendah SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"baca buffer frame"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Mengizinkan apl membaca konten penyangga frame."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"mengonfigurasi tampilan Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Izinkan aplikasi mengonfigurasi dan terhubung ke tampilan Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"mengontrol tampilan Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Mengizinkan aplikasi mengambil, memeriksa, dan menghapus pemberitahuan, termasuk pemberitahuan yang diposkan oleh aplikasi lain."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"mengikat layanan pendengar pemberitahuan"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Memungkinkan pemegang mengikat antarmuka tingkat teratas dari suatu layanan pendengar pemberitahuan. Tidak pernah diperlukan oleh aplikasi normal."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Setel aturan sandi"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrol panjang dan karakter yang diizinkan dalam sandi pembuka layar."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Upaya pembukaan kunci layar monitor"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 83a0bb6..b877501 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Consente all\'applicazione l\'utilizzo di funzioni di basso livello SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lettura buffer di frame"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Consente all\'applicazione di leggere i contenuti del buffer di frame."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurazione di schermi Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Consente all\'applicazione di configurare schermi Wi-Fi e di collegarsi a essi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controllo di schermi Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Consente all\'app di recuperare, esaminare e cancellare notifiche, comprese quelle pubblicate da altre app."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincolo a un servizio listener di notifica"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Consente al titolare di vincolarsi all\'interfaccia di primo livello di un servizio listener di notifica. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Imposta regole password"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlla la lunghezza e i caratteri ammessi nelle password di sblocco dello schermo."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitora tentativi di sblocco dello schermo"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index f864ba5..11ff720 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"מאפשר ליישום להשתמש בתכונות ברמה הנמוכה של SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"אחסון זמני של מסגרת קריאה"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"מאפשר ליישום לקרוא את התוכן של מאגר הנתונים הזמני של המסגרות."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"הגדר תצוגות Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"מאפשר לאפליקציה להגדיר ולהתחבר לתצוגות Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"שלוט בתצוגות Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"מאפשר ליישום לאחזר, לבדוק ולמחוק התראות, כולל כאלה שפורסמו על ידי יישומים אחרים."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"איגוד לשירות של מאזין להתראות"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"הרשאה זו מאפשרת למשתמש לבצע איגוד לממשק הרמה העליונה של שירות מאזין להתראות. הרשאה זו אף פעם אינה נחוצה ליישומים רגילים."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"הגדר כללי סיסמה"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"שלוט באורך ובתווים המותרים בסיסמאות לביטול נעילת מסך."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"עקוב אחר ניסיונות לביטול נעילת מסך"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 3dba541..41c0021 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"SurfaceFlingerの低レベルの機能の使用をアプリに許可します。"</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"フレームバッファの読み取り"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"フレームバッファの内容の読み取りをアプリに許可します。"</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wi-Fiディスプレイの設定"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Wi-Fiディスプレイを設定して接続することをアプリに許可します。"</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wi-Fiディスプレイの制御"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"通知(他のアプリから投稿されたものも含む)を取得、調査、クリアすることをアプリに許可します。"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"通知リスナーサービスにバインド"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"通知リスナーサービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"パスワードルールの設定"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"画面ロック解除パスワードの長さと使用できる文字を制御します。"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"画面ロック解除試行の監視"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 8cafa31..f079520 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"앱이 SurfaceFlinger의 하위 수준 기능을 사용할 수 있도록 허용합니다."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"프레임 버퍼 읽기"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"앱이 프레임 버퍼의 내용을 읽을 수 있도록 허용합니다."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wi-Fi 디스플레이 설정"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"앱이 Wi-Fi 디스플레이를 설정하고 연결하도록 허용합니다."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wi-Fi 디스플레이 제어"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"앱이 다른 앱에서 게시한 알림을 비롯하여 알림을 검색하고 살펴보며 삭제할 수 있도록 허용합니다."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"알림 수신기 서비스 사용"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"권한을 가진 프로그램이 알림 수신기 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"비밀번호 규칙 설정"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"화면 잠금해제 비밀번호에 허용되는 길이 및 문자 수를 제어합니다."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"화면 잠금해제 시도 모니터링"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 6335bb7..14d1af6 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Leidžiama programai naudoti „SurfaceFlinger“ žemo lygio funkcijas."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"skaityti kadrų buferį"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Leidžiama programai skaityti rėmelio buferio turinį."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigūruoti „Wi-Fi“ pateiktis"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Leidžiama programai konfigūruoti ir prisijungti prie „Wi-Fi“ pateikčių."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"valdyti „Wi-Fi“ pateiktis"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Programai leidžiama gauti, patikrinti ir išvalyti pranešimus, įskaitant pranešimus, kuriuos paskelbė kitos programos."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"susisaistyti su pranešimų skaitymo priemonės paslauga"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Leidžiama turėtojui susisaistyti su pranešimų skaitymo priemonės paslaugos aukščiausio lygio sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Nustatyti slaptažodžio taisykles"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Valdyti leidžiamą ekrano atrakinimo slaptažodžių ilgį ir leidžiamus naudoti simbolius."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Stebėti bandymus atrakinti ekraną"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 6d14fed..7da9a73 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Ļauj lietotnei lietot SurfaceFlinger zema līmeņa funkcijas."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lasīt kadru buferi"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Ļauj lietotnei lasīt kadru bufera saturu."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wi-Fi displeju konfigurēšana"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Ļauj lietotnei konfigurēt Wi-Fi displejus un veidot savienojumu ar tiem."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wi-Fi displeju vadība"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ļauj lietotnei izgūt, pārbaudīt un dzēst paziņojumus, tostarp lietotņu publicētos paziņojumus."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"saites izveidošana ar paziņojumu uztvērēja pakalpojumu"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ļauj īpašniekam izveidot saiti ar paziņojumu uztvērēja pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Paroles kārtulu iestatīšana"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolē ekrāna atbloķēšanas parolē atļautās rakstzīmes un garumu."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Ekrāna atbloķēšanas mēģinājumu pārraudzīšana"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index aa6a553..e72085e 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Membenarkan apl menggunakan ciri peringkat rendah SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"baca penimbal bingkai"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Membenarkan apl membaca kandungan penimbal bingkai."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurasikan paparan Wifi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Membenarkan apl mengkonfigurasi dan menyambung ke paparan Wifi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kawal paparan Wifi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Membenarkan apl untuk mendapatkan semula, memeriksa dan memadam bersih pemberitahuan, termasuk yang disiarkan oleh apl lain."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ikat kepada perkhidmatan pendengar pemberitahuan"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan pendengar pemberitahuan. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Tetapkan peraturan kata laluan"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Mengawal panjang dan aksara yang dibenarkan dalam kata laluan buka kunci skrin."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Memantau percubaan buka kunci skrin"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index fdcf826..60a7bef 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Lar appen bruke grunnleggende SurfaceFlinger-funksjoner."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lese skjermbufferet"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Lar appen lese innholdet i rammebufferen."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurere Wi-Fi-skjermer"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Tillater appen å konfigurere og koble til Wi-Fi-skjermer."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kontrollere Wi-Fi-skjermer"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Lar appen hente, gjennomgå og fjerne varsler, inkludert de som sendes fra andre apper."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"binding til en varsellyttertjeneste"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lar innehaveren binde seg til det øverste grensesnittnivået for en varsellyttertjeneste. Skal aldri være nødvendig for vanlige apper."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Angi passordregler"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontroller tillatt lengde og tillatte tegn i passord for opplåsing av skjerm."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Overvåk forsøk på opplåsing av skjerm"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 327337c..1584122 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Hiermee kan de app SurfaceFlinger-functies op laag niveau gebruiken."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"framebuffer lezen"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Hiermee kan de app de inhoud van de framebuffer lezen."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"wifi-displays configureren"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"De app toestaan wifi-displays te configureren en hiermee verbinding te maken."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"wifi-displays beheren"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Hiermee kan de app meldingen ophalen, onderzoeken en wissen, waaronder meldingen die zijn verzonden door andere apps."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"koppelen aan een listener-service voor meldingen"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Hiermee kan de houder koppelen aan de hoofdinterface van een listener-service voor meldingen. Nooit vereist voor normale apps."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Wachtwoordregels instellen"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"De lengte en tekens beheren die zijn toegestaan in wachtwoorden voor schermontgrendeling."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Pogingen voor schermontgrendeling bijhouden"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index c63baab..a56fdca 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Pozwala aplikacji na wykorzystanie funkcji niskiego poziomu usługi SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"czytanie bufora ramki"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Pozwala aplikacji na odczyt zawartości bufora ramki."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurowanie wyświetlaczy Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Zezwala aplikacji na konfigurację wyświetlaczy Wi-Fi i łączenie z nimi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"zarządzanie wyświetlaczami Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Umożliwia aplikacji pobieranie, sprawdzanie i usuwanie powiadomień, także tych, które pochodzą z innych aplikacji."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"utwórz połączenie z usługą odbiornika powiadomień"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Zezwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi odbiornika powiadomień. Nie powinno być nigdy potrzebne dla zwykłych aplikacji."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Określ reguły hasła"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolowanie długości haseł odblokowania ekranu i dozwolonych w nich znaków"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitoruj próby odblokowania ekranu"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index abb2a95..61463d0 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite à aplicação utilizar funcionalidades de SurfaceFlinger de nível inferior."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"ler memória intermédia de fotogramas"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite à aplicação ler o conteúdo da memória intermédia de fotogramas."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurar visores Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite que a aplicação se configure e se ligue a visores Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlar visores Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que a aplicação obtenha, examine e limpe notificações, incluindo as que foram publicadas por outras aplicações."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincular a um serviço de escuta de notificações"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite que o titular vincule a interface de nível superior de um serviço de escuta de notificações. Nunca deverá ser necessário para aplicações normais."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras de palavra-passe"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar o comprimento e os caracteres permitidos nas palavras-passe de desbloqueio do ecrã."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizar tentativas de desbloqueio do ecrã"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 5cd4710..70392c0 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite que o aplicativo use recursos com baixos níveis de SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"ler o buffer do frame"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite que o aplicativo leia o conteúdo do buffer de frame."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurar monitores Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite que o aplicativo configure e conecte a monitores Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlar monitores Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que o aplicativo recupere, examine e limpe notificações, inclusive as postadas por outros aplicativos."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"sujeitar a um serviço ouvinte de notificações"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite que o proprietário sujeite a interface de nível superior a um serviço ouvinte de notificações. Não deve ser necessário para aplicativos comuns."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras para senha"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Controle o tamanho e os caracteres permitidos nas senhas de desbloqueio de tela."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitorar tentativas de desbloqueio da tela"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 6e578c7..06d0afb 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -757,6 +757,10 @@
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"leger il paraculp da frame"</string>
<!-- no translation found for permdesc_readFrameBuffer (4937405521809454680) -->
<skip />
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<!-- no translation found for permlab_configureWifiDisplay (5595661694746742168) -->
<skip />
<!-- no translation found for permdesc_configureWifiDisplay (7916815158690218065) -->
@@ -1076,6 +1080,10 @@
<skip />
<!-- no translation found for permdesc_bindNotificationListenerService (985697918576902986) -->
<skip />
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<!-- no translation found for policylab_limitPassword (4497420728857585791) -->
<skip />
<!-- no translation found for policydesc_limitPassword (3252114203919510394) -->
@@ -2538,6 +2546,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 42c7ca3..96c6f47 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite aplicaţiei să utilizeze funcţiile de nivel redus SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"citire zonă tampon de cadre"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite aplicaţiei să citească conţinutul zonei-tampon a cadrului."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurează afişaje Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite aplicaţiei să configureze şi să se conecteze la afişaje Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlează afişaje Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite aplicației să recupereze, să examineze și să șteargă notificări, inclusiv pe cele postate de alte aplicații."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"conectare la un serviciu de citire a notificărilor"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu de citire a notificărilor. În mod normal aplicațiile nu ar trebui să aibă nevoie de această permisiune."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Setaţi reguli pentru parolă"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Stabiliţi lungimea şi tipul de caractere permise în parolele pentru deblocarea ecranului."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizaţi încercările de deblocare a ecranului"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index b2dd07d..75d2e68 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Приложение сможет использовать низкоуровневые функции SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Чтение данных в буфере кадров"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Приложение сможет считывать содержание буфера фреймов."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"настраивать экраны, подключенные через Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Приложение сможет подключаться к экранам с помощью Wi-Fi и настраивать их."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Управление мониторами, подключенными через Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Приложение сможет получать, проверять и удалять уведомления, включая те, что опубликованы другими приложениями."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"Подключение к службе просмотра уведомлений"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Приложение сможет подключаться к базовому интерфейсу службы просмотра уведомлений. Это разрешение не используется обычными приложениями."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Правила выбора паролей"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролировать длину и символы при вводе паролей для снятия блокировки экрана."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Отслеживать попытки снятия блокировки экрана"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index dd30fb4..3806936 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Umožňuje aplikácii používať funkcie nízkej úrovne aplikácie SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"čítanie vyrovnávacej pamäte snímok"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Umožňuje aplikácii čítať obsah vyrovnávacej pamäte snímok."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurovať displeje cez sieť Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Umožňuje aplikácii konfigurovať displeje a pripojiť sa k nim cez siete Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"ovládať displeje cez sieť Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikácii načítať, zobrazovať a mazať upozornenia vrátane tých, ktoré boli uverejnené inými aplikáciami."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"naviazanie sa na službu na počúvanie upozornení"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Umožňuje držiteľovi naviazať sa na najvyššiu úroveň služby na počúvanie upozornení. Bežné aplikácie by toto nastavenie nemali nikdy požadovať."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Nastaviť pravidlá pre heslo"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Ovládanie dĺžky hesiel na odomknutie obrazovky a v nich používané znaky."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Sledovať pokusy o odomknutie obrazovky"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 46fa56a..fcc72bf 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Programu omogoča uporabo funkcij nizke ravni SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"branje grafičnega/slikovnega medpomnilnika"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Programu omogoča branje vsebine grafičnega/slikovnega medpomnilnika."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfiguriranje zaslonov Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Omogoča aplikaciji konfiguriranje zaslonov Wi-Fi in povezovanje z njimi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"nadzor zaslonov Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Dovoli aplikaciji, da prenese, razišče in izbriše obvestila, tudi tista, ki so jih objavile druge aplikacije."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"poveži se s storitvijo poslušalca obvestil"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lastniku omogoča povezovanje z vmesnikom storitve poslušalca obvestil najvišje ravni. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Nastavitev pravil za geslo"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Nadzor nad dolžino in znaki, ki so dovoljeni v geslih za odklepanje zaslona."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"nadzor nad poskusi odklepanja zaslona"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 79626f8..67de984 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Дозвољава апликацији да користи SurfaceFlinger функције ниског нивоа."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"читање бафера кадрова"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Дозвољава апликацији да чита садржај међумеморије кадрова."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"конфигурисање Wi-Fi екрана"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Дозвољава апликацији да конфигурише Wi-Fi екране и повезује се са њима."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"контрола Wi-Fi екрана"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозвољава апликацији да преузима, испитује и брише обавештења, укључујући она која постављају друге апликације."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"повезивање са услугом монитора обавештења"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дозвољава власнику да се повеже са интерфејсом услуге монитора обавештења највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Подешавање правила за лозинку"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролишите дужину и знакове дозвољене у лозинкама за откључавање екрана."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Надгледање покушаја откључавања екрана"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 1618e2f..5da8a63 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Tillåter att appen använder lågnivåfunktioner i SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"läsa rambuffert"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Tillåter att appen läser innehållet i rambufferten."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurerar Wi-Fi-skärmar"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Tillåter att appen konfigurerar och ansluter till Wi-Fi-skärmar."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kontrollerar Wi-Fi-skärmar"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillåter att appen hämtar, granskar och raderar meddelanden, även sådana som skickats av andra appar."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"binda till en meddelandelyssnare"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en meddelandelyssnare. Ska inte behövas för vanliga appar."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Ange lösenordsregler"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Bestäm hur många och vilka tecken som är tillåtna i skärmlåsets lösenord."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Övervaka försök att låsa upp skärmen"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 14fbef8..a6f1ee3 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Inaruhusu programu kutumia vipengee vya kiwango cha chini vya SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"soma bafa ya fremu"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Inaruhusu programu kusoma maudhui ya fremu ya bafa."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"sanidi maonyesho ya Wifi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Inaruhusu programu kusanidi na kuunganika kwenye maonyesho ya Wifi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"dhibiti maonyesho ya Wifi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Huruhusu programu kurejesha, kuchunguza, na kuondoa arifa, ikiwa ni pamoja na zile zilizochapishwa na programu nyingine."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"unganisha kwenye huduma ya kisikilizi cha arifa"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Inaruhusu kishikilizi kuunganishwa kwenye kusano cha kiwango cha juu cha huduma ya kisikilizi cha arifa. Haipaswi kuhitajika tena kwa programu za kawaida."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Weka kanuni za nenosiri"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Dhibiti urefu na vibambo vinavyoruhusiwa katika manenosiri ya kufungua skrini."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Chunguza majaribio ya kutofun gua skrini"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7d804ca..f1d4c3c 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"อนุญาตให้แอปพลิเคชันใช้คุณลักษณะระดับต่ำของ SurfaceFlinger"</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"อ่านเฟรมบัฟเฟอร์"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"อนุญาตให้แอปพลิเคชันอ่านเนื้อหาในเฟรมบัฟเฟอร์"</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"กำหนดค่าการแสดงผลด้วย WiFi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"อนุญาตให้แอปกำหนดค่าและเชื่อมต่อกับจอแสดงผล WiFi ได้"</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"ควบคุมการแสดงผลด้วย WiFi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"ทำให้แอปสามารถเรียกดู ตรวจสอบ และล้างการแจ้งเตือนได้ ซึ่งรวมถึงการแจ้งเตือนที่โพสต์โดยแอปอื่นๆ ด้วย"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"เชื่อมโยงกับบริการตัวฟังการแจ้งเตือน"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"อนุญาตให้เจ้าของเชื่อมโยงกับอินเตอร์เฟซระดับสูงสุดของบริการตัวฟังการแจ้งเตือน ซึ่งไม่มีความจำเป็นสำหรับแอปธรรมดา"</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"ตั้งค่ากฎรหัสผ่าน"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"ควบคุมความยาวและอักขระที่อนุญาตให้ใช้ในรหัสผ่านการปลดล็อกหน้าจอ"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 50aea21..2d9ed63 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Pinapayagan ang app na gamitin ang mababang antas na mga tampok ng SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"basahin ang buffer ng frame"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Pinapayagan ang app na basahin ang nilalaman ng buffer ng frame."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"mag-configure ng mga Wifi display"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Pinapayagan ang app na mag-configure at kumonekta sa mga Wifi display."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"magkontrol ng mga Wifi display"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Pinapayagan ang app na kumuha, sumuri, at mag-clear ng mga notification, kabilang ang mga na-post ng iba pang apps."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"mapailalim sa isang serbisyo ng notification listener"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Nagbibigay-daan sa may-ari na mapailalim sa interface sa tuktok na antas ng isang serbisyo ng notification listener. Hindi dapat kailanganin para sa karaniwang apps kahit kailan."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Magtakda ng mga panuntunan sa password"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolin ang haba at mga character na pinapayagan sa mga password sa pag-unlock ng screen."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Subaybayan ang mga pagsubok sa pag-unlock ng screen"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 167b672..87c3c48 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Uygulamaya, SurfaceFlinger\'a ait düşük düzey özellikleri kullanma izni verir."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"çerçeve arabelleğini oku"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Uygulamaya, çerçeve arabelleğinin içeriğini okuma izni verir."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Kablosuz ekranları yapılandır"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Uygulamaya kablosuz ekranları yapılandırma ve bunlara bağlanma izni verir."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Kablosuz ekranları denetle"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Uygulamanın bildirimler almasına, bildirimleri incelemesine ve temizlemesine izin verir. Buna diğer uygulamalar tarafından yayınlanan bildirimler de dahildir."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bildirim dinleyici hizmetine bağlan"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"İzin sahibine bir bildirim dinleyici hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Şifre kuralları ayarla"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Ekran kilidini açma şifrelerinde izin verilen uzunluğu ve karakterleri denetleme."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Ekran kilidini açma denemelerini izle"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index a94925c..099fb91 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Дозволяє програмі використовувати низькорівневі функції SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"читати фрейм-буфер"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Дозволяє програмі читати вміст буфера кадрів."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"налаштувати екрани Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Дозволяє програмі налаштовувати екрани Wi-Fi і під’єднуватися до них."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"керувати екранами Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозволяє програмі отримувати, перевіряти й очищати сповіщення, зокрема опубліковані іншими програмами."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"прив’язуватися до служби читання сповіщень"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дозволяє власнику прив’язуватися до інтерфейсу верхнього рівня служби читання сповіщень. Ніколи не застосовується для звичайних програм."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Устан. правила пароля"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролювати довжину паролів для розблокування екрана та дозволені в них символи."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Відстежув. спроби розблок. екрана"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 373fe3a..6f339e6 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Cho phép ứng dụng sử dụng các tính năng SurfaceFlinger cấp độ thấp."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"đọc bộ đệm khung"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Cho phép ứng dụng đọc nội dung của bộ đệm khung."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"định cấu hình màn hình Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Cho phép ứng dụng định cấu hình và kết nối với màn hình Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kiểm soát màn hình Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Cho phép ứng dụng truy xuất, kiểm tra và xóa thông báo, bao gồm những thông báo được đăng bởi các ứng dụng khác."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"liên kết với dịch vụ trình xử lý thông báo"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ trình xử lý thông báo. Không cần thiết cho các ứng dụng thông thường."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Đặt quy tắc mật khẩu"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Kiểm soát độ dài và ký tự được phép trong mật khẩu mở khóa màn hình."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Giám sát những lần thử mở khóa màn hình"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 21fdaf16..d39cd3f 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"允许应用使用 SurfaceFlinger 低级功能。"</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"读取帧缓冲区"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"允许应用读取帧缓冲区的内容。"</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"配置 WLAN 显示设备"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"允许应用配置并连接到 WLAN 显示设备。"</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"控制 WLAN 显示设备"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"允许该应用检索、检查并清除通知,包括其他应用发布的通知。"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"绑定到通知侦听器服务"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允许应用绑定到通知侦听器服务的顶级接口(普通应用绝不需要此权限)。"</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"设置密码规则"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"控制屏幕解锁密码所允许的长度和字符。"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"监视屏幕解锁尝试次数"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index e627034..a9f7b7b 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"允許應用程式使用 SurfaceFlinger 的低階功能。"</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"讀取框架緩衝"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"允許應用程式讀取畫面緩衝區的內容。"</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"設定 Wi-Fi 顯示裝置"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"允許應用程式設定及連接 Wi-Fi 顯示裝置。"</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"控制 Wi-Fi 顯示裝置"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"允許應用程式擷取、檢查及清除通知 (包括由其他應用程式發佈的通知)。"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"繫結至通知接聽器服務"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允許應用程式繫結至通知接聽器服務的頂層介面 (一般應用程式不需使用)。"</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"設定密碼規則"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"控制螢幕解鎖密碼所允許的長度和字元。"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"監視螢幕解鎖嘗試次數"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index af361ac..2957366 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -470,6 +470,10 @@
<string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Ivumela insiza ukuthi isebenzise okuqukethwe i-SurfaceFlinger okusezingeni eliphansi."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"funda isikhumbuli sesikhashana sendikimba"</string>
<string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Ivumela insiza ukuthi ifunde okuqukethwe ifreyimu yebhafa."</string>
+ <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+ <skip />
+ <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+ <skip />
<string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"lungisa ukubukwa kwe-Wi-Fi"</string>
<string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Ivumela uhlelo lokusebenza ukulungisa nokuxhuma ekubukisweni kwe-Wi-Fi."</string>
<string name="permlab_controlWifiDisplay" msgid="393641276723695496">"lawula ukubukwa kwe-Wi-Fi"</string>
@@ -647,6 +651,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ivumela uhlelo lokusebenza ukuthi lithole, lihlole, liphinde lisuse izaziso, ezifaka lezo ezithunyelwe ezinye izinhlelo zokusebenza."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bophezela kwisevisi yomlaleli wesaziso"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ivumela umbambi ukubophezela kwisixhumi esibonakalayo sezinga eliphezulu lesevisi yomlaleli wesaziso. Akusoze kwadingeka kwizinhlelo zokusebenza ezivamile."</string>
+ <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+ <skip />
+ <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+ <skip />
<string name="policylab_limitPassword" msgid="4497420728857585791">"Misa imithetho yephasiwedi"</string>
<string name="policydesc_limitPassword" msgid="3252114203919510394">"Lawula ubude nezinhlamvu ezivunyelwe kumaphasiwedi okuvula isikrini"</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Gaka imizamo yokuvula isikrini"</string>
@@ -1597,6 +1605,10 @@
<skip />
<!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
<skip />
+ <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+ <skip />
+ <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+ <skip />
<!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
<skip />
<!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 194da10..181793f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2578,6 +2578,31 @@
<attr name="vendor" format="string"/>
</declare-styleable>
+ <!-- Use <code>apdu-service</code> as the root tag of the XML resource that
+ describes an {@link android.nfc.cardemulation.HostApduService} or
+ {@link android.nfc.cardemulation.SeApduService} service, which is referenced
+ from its SERVICE_META_DATA entry. -->
+ <declare-styleable name="ApduService">
+ <!-- Set to true to let the NFC subsystem know that this service implements
+ a payment instrument. That will allow this service to be enumerated in
+ a list of payment services, where the user can pick his preferred payment
+ service. The preferred service will be bound to persistently, to make sure
+ it can immediately process APDUs without service startup delay. This is vital
+ for existing payment infrastructure that has very strict timing requirements. -->
+ <attr name="paymentService" format="boolean" />
+ <!-- Short description of the functionality the serivce implements.-->
+ <attr name="description" />
+ </declare-styleable>
+
+ <!-- Specify one or more <code>aid-filter</code> elements inside a <code>apdu-service</code>
+ element to list the ISO7816 Application ID (AIDs) your service can handle.-->
+ <declare-styleable name="AidFilter">
+ <!-- The ISO7816 Application ID -->
+ <attr name="aid" format="string" />
+ <!-- Short description of what the AID implements.-->
+ <attr name="description" />
+ </declare-styleable>
+
<declare-styleable name="ActionMenuItemView">
<attr name="minWidth" />
</declare-styleable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3ab8825..e5fcfb0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2068,5 +2068,6 @@
<public type="attr" name="sspPattern" />
<public type="attr" name="addPrintersActivity" />
<public type="attr" name="vendor" />
-
+ <public type="attr" name="paymentService" />
+ <public type="attr" name="aid" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index eba3f42..b01b50e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4256,12 +4256,16 @@
<!-- Write fail reason: couldn't write the printed content. [CHAR LIMIT=none] -->
<string name="write_fail_reason_cannot_write">Error writing content</string>
+ <!-- PIN entry dialog label/hint for PIN [CHAR LIMIT=none] -->
+ <string name="restr_pin_enter_pin">Enter PIN</string>
+ <!-- PIN entry dialog label/hint for old PIN [CHAR LIMIT=none] -->
+ <string name="restr_pin_enter_old_pin">Current PIN</string>
+ <!-- PIN entry dialog label for new PIN [CHAR LIMIT=none] -->
+ <string name="restr_pin_enter_new_pin">New PIN</string>
+ <!-- PIN entry dialog label for new PIN confirmation [CHAR LIMIT=none] -->
+ <string name="restr_pin_confirm_pin">Confirm new PIN</string>
<!-- PIN creation dialog message [CHAR LIMIT=none] -->
<string name="restr_pin_create_pin">Create a PIN for modifying restrictions</string>
- <!-- PIN entry dialog label for PIN [CHAR LIMIT=none] -->
- <string name="restr_pin_enter_pin">Enter PIN</string>
- <!-- PIN entry dialog label for PIN confirmation [CHAR LIMIT=none] -->
- <string name="restr_pin_confirm_pin">Confirm PIN</string>
<!-- PIN entry dialog error when PINs are not the same [CHAR LIMIT=none] -->
<string name="restr_pin_error_doesnt_match">PINs don\'t match. Try again.</string>
<!-- PIN entry dialog error when PIN is too short [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e29e82b..8a12ac8 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -214,8 +214,9 @@
<java-symbol type="id" name="breadcrumb_section" />
<java-symbol type="id" name="action_bar_spinner" />
<java-symbol type="id" name="pin_message" />
- <java-symbol type="id" name="pin1_text" />
- <java-symbol type="id" name="pin2_text" />
+ <java-symbol type="id" name="pin_text" />
+ <java-symbol type="id" name="pin_new_text" />
+ <java-symbol type="id" name="pin_confirm_text" />
<java-symbol type="id" name="pin_error_message" />
<java-symbol type="attr" name="actionModeShareDrawable" />
@@ -1157,7 +1158,8 @@
<java-symbol type="layout" name="sms_short_code_confirmation_dialog" />
<java-symbol type="layout" name="action_bar_up_container" />
<java-symbol type="layout" name="app_not_authorized" />
- <java-symbol type="layout" name="pin_challenge" />
+ <java-symbol type="layout" name="restrictions_pin_challenge" />
+ <java-symbol type="layout" name="restrictions_pin_setup" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />
diff --git a/core/res/res/xml/audio_assets.xml b/core/res/res/xml/audio_assets.xml
index 746dbab..af5798a 100644
--- a/core/res/res/xml/audio_assets.xml
+++ b/core/res/res/xml/audio_assets.xml
@@ -34,5 +34,6 @@
<asset id="FX_KEYPRESS_SPACEBAR" file="KeypressSpacebar.ogg"/>
<asset id="FX_KEYPRESS_DELETE" file="KeypressDelete.ogg"/>
<asset id="FX_KEYPRESS_RETURN" file="KeypressReturn.ogg"/>
+ <asset id="FX_KEYPRESS_INVALID" file="KeypressInvalid.ogg"/>
</group>
</audio_assets>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 1289971..ec8e7ea 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -54,10 +54,6 @@
<group gid="inet" />
</permission>
- <permission name="android.permission.CAMERA" >
- <group gid="camera" />
- </permission>
-
<permission name="android.permission.READ_LOGS" >
<group gid="log" />
</permission>
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index 8b03bf7..ed9bea5 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -221,6 +221,7 @@
$(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/KeypressInvalid_120_48k.ogg:system/media/audio/ui/KeypressInvalid.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 \
diff --git a/data/sounds/AudioPackage11.mk b/data/sounds/AudioPackage11.mk
index e16a92d..2897b04 100644
--- a/data/sounds/AudioPackage11.mk
+++ b/data/sounds/AudioPackage11.mk
@@ -43,7 +43,7 @@
$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.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/Tejat_proc48.ogg:system/media/audio/notifications/Tejat.ogg \
$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.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 \
diff --git a/data/sounds/effects/KeypressInvalid.ogg b/data/sounds/effects/KeypressInvalid.ogg
new file mode 100644
index 0000000..24935ad
--- /dev/null
+++ b/data/sounds/effects/KeypressInvalid.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressInvalid.wav b/data/sounds/effects/KeypressInvalid.wav
new file mode 100644
index 0000000..c4180e35
--- /dev/null
+++ b/data/sounds/effects/KeypressInvalid.wav
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressInvalid_120_48k.ogg b/data/sounds/effects/ogg/KeypressInvalid_120_48k.ogg
new file mode 100644
index 0000000..24935ad
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressInvalid_120_48k.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Tejat_proc48.ogg b/data/sounds/notifications/ogg/Tejat_proc48.ogg
new file mode 100644
index 0000000..b1637d7
--- /dev/null
+++ b/data/sounds/notifications/ogg/Tejat_proc48.ogg
Binary files differ
diff --git a/data/sounds/notifications/wav/Deneb_processed_48kHz.wav b/data/sounds/notifications/wav/Deneb_processed_48kHz.wav
new file mode 100644
index 0000000..e2df8e9
--- /dev/null
+++ b/data/sounds/notifications/wav/Deneb_processed_48kHz.wav
Binary files differ
diff --git a/docs/html/about/versions/android-4.3.jd b/docs/html/about/versions/android-4.3.jd
index 0ca3bc6..d0ccfbe 100644
--- a/docs/html/about/versions/android-4.3.jd
+++ b/docs/html/about/versions/android-4.3.jd
@@ -7,7 +7,7 @@
<div id="qv-wrapper">
<div id="qv">
-
+
<h2>In this document
<a href="#" onclick="hideNestedItems('#toc43',this);return false;" class="header-toggle">
<span class="more">show more</span>
@@ -62,7 +62,6 @@
</li>
<li><a href="#UserInput">User Input</a>
<ol>
- <li><a href="#SignificantMotion">Detect significant motion</a></li>
<li><a href="#Sensors">New sensor types</a></li>
</ol>
</li>
@@ -133,7 +132,7 @@
Then build your apps against the Android {@sdkPlatformVersion} platform to begin using the
latest APIs.</p>
-
+
<h3 id="ApiLevel">Update your target API level</h3>
<p>To better optimize your app for devices running Android {@sdkPlatformVersion},
@@ -145,7 +144,7 @@
<p>You can use APIs in Android {@sdkPlatformVersion} while also supporting older versions by adding
conditions to your code that check for the system API level before executing
APIs not supported by your <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a>.
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a>.
To learn more about maintaining backward compatibility, read <a
href="{@docRoot}training/basics/supporting-devices/platforms.html">Supporting Different
Platform Versions</a>.</p>
@@ -172,11 +171,11 @@
<p>Your app might misbehave in a restricted profile environment.</p>
-<p>Users in a <a href="#RestrictedProfiles">restricted profile</a> environment might not
-have all the standard Android apps available. For example, a restricted profile might have the
-web browser and camera app disabled. So your app should not make assumptions about which apps are
-available, because if you call {@link android.app.Activity#startActivity startActivity()} without
-verifying whether an app is available to handle the {@link android.content.Intent},
+<p>Users in a <a href="#RestrictedProfiles">restricted profile</a> environment might not
+have all the standard Android apps available. For example, a restricted profile might have the
+web browser and camera app disabled. So your app should not make assumptions about which apps are
+available, because if you call {@link android.app.Activity#startActivity startActivity()} without
+verifying whether an app is available to handle the {@link android.content.Intent},
your app might crash in a restricted profile.</p>
<p>When using an implicit intent, you should always verify that an app is available to handle the intent by calling {@link android.content.Intent#resolveActivity resolveActivity()} or {@link android.content.pm.PackageManager#queryIntentActivities queryIntentActivities()}. For example:</p>
@@ -197,20 +196,20 @@
<p>Your app might misbehave in a restricted profile environment.</p>
<p>Users within a restricted profile environment do not have access to user accounts by default.
-If your app depends on an {@link android.accounts.Account}, then your app might crash or behave
+If your app depends on an {@link android.accounts.Account}, then your app might crash or behave
unexpectedly when used in a restricted profile.</p>
<p>If you'd like to prevent restricted profiles from using your app entirely because your
-app depends on account information that's sensitive, specify the <a
-href="{@docRoot}guide/topics/manifest/application-element.html#requiredAccountType">{@code
-android:requiredAccountType}</a> attribute in your manifest's <a
-href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
+app depends on account information that's sensitive, specify the <a
+href="{@docRoot}guide/topics/manifest/application-element.html#requiredAccountType">{@code
+android:requiredAccountType}</a> attribute in your manifest's <a
+href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
element.</p>
-<p>If you’d like to allow restricted profiles to continue using your app even though they can’t
-create their own accounts, then you can either disable your app features that require an account
+<p>If you’d like to allow restricted profiles to continue using your app even though they can’t
+create their own accounts, then you can either disable your app features that require an account
or allow restricted profiles to access the accounts created by the primary user. For more
-information, see the section
+information, see the section
below about <a href="#AccountsInProfile">Supporting accounts in a restricted profile</a>.</p>
@@ -218,67 +217,67 @@
<h2 id="RestrictedProfiles">Restricted Profiles</h2>
-<p>On Android tablets, users can now create restricted profiles based on the primary user.
+<p>On Android tablets, users can now create restricted profiles based on the primary user.
When users create a restricted profile, they can enable restrictions such as which apps are
available to the profile. A new set of APIs in Android 4.3 also allow you to build fine-grain
-restriction settings for the apps you develop. For example, by using the new APIs, you can
-allow users to control what type of content is available within your app when running in a
+restriction settings for the apps you develop. For example, by using the new APIs, you can
+allow users to control what type of content is available within your app when running in a
restricted profile environment.</p>
-<p>The UI for users to control the restrictions you've built is managed by the system's
+<p>The UI for users to control the restrictions you've built is managed by the system's
Settings application. To make your app's restriction settings appear to the user,
-you must declare the restrictions your app provides by creating a {@link
-android.content.BroadcastReceiver} that receives the {@link android.content.Intent#ACTION_GET_RESTRICTION_ENTRIES} intent. The system invokes this intent to query
-all apps for available restrictions, then builds the UI to allow the primary user to
+you must declare the restrictions your app provides by creating a {@link
+android.content.BroadcastReceiver} that receives the {@link android.content.Intent#ACTION_GET_RESTRICTION_ENTRIES} intent. The system invokes this intent to query
+all apps for available restrictions, then builds the UI to allow the primary user to
manage restrictions for each restricted profile. </p>
-<p>In the {@link android.content.BroadcastReceiver#onReceive onReceive()} method of
-your {@link android.content.BroadcastReceiver}, you must create a {@link
-android.content.RestrictionEntry} for each restriction your app provides. Each {@link
-android.content.RestrictionEntry} defines a restriction title, description, and one of the
+<p>In the {@link android.content.BroadcastReceiver#onReceive onReceive()} method of
+your {@link android.content.BroadcastReceiver}, you must create a {@link
+android.content.RestrictionEntry} for each restriction your app provides. Each {@link
+android.content.RestrictionEntry} defines a restriction title, description, and one of the
following data types:</p>
<ul>
- <li>{@link android.content.RestrictionEntry#TYPE_BOOLEAN} for a restriction that is
+ <li>{@link android.content.RestrictionEntry#TYPE_BOOLEAN} for a restriction that is
either true or false.
- <li>{@link android.content.RestrictionEntry#TYPE_CHOICE} for a restriction that has
+ <li>{@link android.content.RestrictionEntry#TYPE_CHOICE} for a restriction that has
multiple choices that are mutually exclusive (radio button choices).
- <li>{@link android.content.RestrictionEntry#TYPE_MULTI_SELECT} for a restriction that
+ <li>{@link android.content.RestrictionEntry#TYPE_MULTI_SELECT} for a restriction that
has multiple choices that are <em>not</em> mutually exclusive (checkbox choices).
</ul>
-<p>You then put all the {@link android.content.RestrictionEntry} objects into an {@link
-java.util.ArrayList} and put it into the broadcast receiver's result as the value for the
+<p>You then put all the {@link android.content.RestrictionEntry} objects into an {@link
+java.util.ArrayList} and put it into the broadcast receiver's result as the value for the
{@link android.content.Intent#EXTRA_RESTRICTIONS_LIST} extra.</p>
-<p>The system creates the UI for your app's restrictions in the Settings app and saves each
-restriction with the unique key you provided for each {@link android.content.RestrictionEntry}
-object. When the user opens your app, you can query for any current restrictions by
-calling {@link android.os.UserManager#getApplicationRestrictions getApplicationRestrictions()}.
+<p>The system creates the UI for your app's restrictions in the Settings app and saves each
+restriction with the unique key you provided for each {@link android.content.RestrictionEntry}
+object. When the user opens your app, you can query for any current restrictions by
+calling {@link android.os.UserManager#getApplicationRestrictions getApplicationRestrictions()}.
This returns a {@link android.os.Bundle} containing the key-value pairs for each restriction
you defined with the {@link android.content.RestrictionEntry} objects.</p>
-<p>If you want to provide more specific restrictions that can't be handled by boolean, single
-choice, and multi-choice values, then you can create an activity where the user can specify the
-restrictions and allow users to open that activity from the restriction settings. In your
-broadcast receiver, include the {@link android.content.Intent#EXTRA_RESTRICTIONS_INTENT} extra
+<p>If you want to provide more specific restrictions that can't be handled by boolean, single
+choice, and multi-choice values, then you can create an activity where the user can specify the
+restrictions and allow users to open that activity from the restriction settings. In your
+broadcast receiver, include the {@link android.content.Intent#EXTRA_RESTRICTIONS_INTENT} extra
in the result {@link android.os.Bundle}. This extra must specify an {@link android.content.Intent}
-indicating the {@link android.app.Activity} class to launch (use the
-{@link android.os.Bundle#putParcelable putParcelable()} method to pass {@link
+indicating the {@link android.app.Activity} class to launch (use the
+{@link android.os.Bundle#putParcelable putParcelable()} method to pass {@link
android.content.Intent#EXTRA_RESTRICTIONS_INTENT} with the intent).
-When the primary user enters your activity to set custom restrictions, your
-activity must then return a result containing the restriction values in an extra using either
-the {@link android.content.Intent#EXTRA_RESTRICTIONS_LIST} or {@link
+When the primary user enters your activity to set custom restrictions, your
+activity must then return a result containing the restriction values in an extra using either
+the {@link android.content.Intent#EXTRA_RESTRICTIONS_LIST} or {@link
android.content.Intent#EXTRA_RESTRICTIONS_BUNDLE} key, depending on whether you specify
{@link android.content.RestrictionEntry} objects or key-value pairs, respectively.</p>
<h3 id="AccountsInProfile">Supporting accounts in a restricted profile</h3>
-<p>Any accounts added to the primary user are available to a restricted profile, but the
-accounts are not accessible from the {@link android.accounts.AccountManager} APIs by default.
+<p>Any accounts added to the primary user are available to a restricted profile, but the
+accounts are not accessible from the {@link android.accounts.AccountManager} APIs by default.
If you attempt to add an account with {@link android.accounts.AccountManager} while in a restricted
-profile, you will get a failure result. Due to these restrictions, you have the following
+profile, you will get a failure result. Due to these restrictions, you have the following
three options:</p>
<li><strong>Allow access to the owner’s accounts from a restricted profile.</strong>
@@ -289,21 +288,25 @@
android:restrictedAccountType="com.example.account.type" >
</pre>
-<p class="caution"><strong>Caution:</strong> Enabling this attribute provides your
-app access to the primary user's accounts from restricted profiles. So you should allow this
+<p class="caution"><strong>Caution:</strong> Enabling this attribute provides your
+app access to the primary user's accounts from restricted profiles. So you should allow this
only if the information displayed by your app does not reveal personally identifiable
-information (PII) that’s considered sensitive.</p>
+information (PII) that’s considered sensitive. The system settings will inform the primary
+user that your app grants restricted profiles to their accounts, so it should be clear to the user
+that account access is important for your app's functionality. If possible, you should also
+provide adequate restriction controls for the primary user that define how much account access
+is allowed in your app.</p>
</li>
<li><strong>Disable certain functionality when unable to modify accounts.</strong>
-<p>If you want to use accounts, but don’t actually require them for your app’s primary
-functionality, you can check for account availability and disable features when not available.
-You should first check if there is an existing account available. If not, then query whether
-it’s possible to create a new account by calling {@link
-android.os.UserManager#getUserRestrictions()} and check the {@link
-android.os.UserManager#DISALLOW_MODIFY_ACCOUNTS} extra in the result. If it is {@code true},
-then you should disable whatever functionality of your app requires access to accounts.
+<p>If you want to use accounts, but don’t actually require them for your app’s primary
+functionality, you can check for account availability and disable features when not available.
+You should first check if there is an existing account available. If not, then query whether
+it’s possible to create a new account by calling {@link
+android.os.UserManager#getUserRestrictions()} and check the {@link
+android.os.UserManager#DISALLOW_MODIFY_ACCOUNTS} extra in the result. If it is {@code true},
+then you should disable whatever functionality of your app requires access to accounts.
For example:</p>
<pre>
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -312,15 +315,15 @@
// cannot add accounts, disable some functionality
}
</pre>
-<p class="note"><strong>Note:</strong> In this scenario, you should <em>not</em> declare
+<p class="note"><strong>Note:</strong> In this scenario, you should <em>not</em> declare
any new attributes in your manifest file.</p>
</li>
<li><strong>Disable your app when unable to access private accounts.</strong>
-<p>If it’s instead important that your app not be available to restricted profiles because
-your app depends on sensitive personal information in an account (and because restricted profiles
+<p>If it’s instead important that your app not be available to restricted profiles because
+your app depends on sensitive personal information in an account (and because restricted profiles
currently cannot add new accounts), add
-the <a href="{@docRoot}guide/topics/manifest/application-element.html#requiredAccountType">{@code
+the <a href="{@docRoot}guide/topics/manifest/application-element.html#requiredAccountType">{@code
android:requiredAccountType}</a> attribute to the <a
href="{@docRoot}guide/topics/manifest/application-element.html"><application></a> tag:</p>
<pre>
@@ -338,11 +341,11 @@
<h3 id="BTLE">Bluetooth Low Energy (Smart Ready)</h3>
-<p>Android now supports Bluetooth Low Energy (LE) with new APIs in {@link android.bluetooth}.
-With the new APIs, you can build Android apps that communicate with Bluetooth Low Energy
+<p>Android now supports Bluetooth Low Energy (LE) with new APIs in {@link android.bluetooth}.
+With the new APIs, you can build Android apps that communicate with Bluetooth Low Energy
peripherals such as heart rate monitors and pedometers.</p>
-<p>Because Bluetooth LE is a hardware feature that is not available on all
+<p>Because Bluetooth LE is a hardware feature that is not available on all
Android-powered devices, you must declare in your manifest file a <a
href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a>
element for {@code "android.hardware.bluetooth_le"}:</p>
@@ -350,11 +353,11 @@
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
</pre>
-<p>If you're already familiar with Android's Classic Bluetooth APIs, notice that using the
-Bluetooth LE APIs has some differences. Most importantly is that there's now a {@link
-android.bluetooth.BluetoothManager} class that you should use for some high level operations
-such as acquiring a {@link android.bluetooth.BluetoothAdapter}, getting a list of connected
-devices, and checking the state of a device. For example, here's how you should now get the
+<p>If you're already familiar with Android's Classic Bluetooth APIs, notice that using the
+Bluetooth LE APIs has some differences. Most importantly is that there's now a {@link
+android.bluetooth.BluetoothManager} class that you should use for some high level operations
+such as acquiring a {@link android.bluetooth.BluetoothAdapter}, getting a list of connected
+devices, and checking the state of a device. For example, here's how you should now get the
{@link android.bluetooth.BluetoothAdapter}:</p>
<pre>
final BluetoothManager bluetoothManager =
@@ -362,32 +365,32 @@
mBluetoothAdapter = bluetoothManager.getAdapter();
</pre>
-<p>To discover Bluetooth LE peripherals, call {@link android.bluetooth.BluetoothAdapter#startLeScan
-startLeScan()} on the {@link android.bluetooth.BluetoothAdapter}, passing it an implementation
-of the {@link android.bluetooth.BluetoothAdapter.LeScanCallback} interface. When the Bluetooth
-adapter detects a Bluetooth LE peripheral, your {@link
-android.bluetooth.BluetoothAdapter.LeScanCallback} implementation receives a call to the
-{@link android.bluetooth.BluetoothAdapter.LeScanCallback#onLeScan onLeScan()} method. This
-method provides you with a {@link android.bluetooth.BluetoothDevice} object representing the
-detected device, the RSSI value for the device, and a byte array containing the device's
+<p>To discover Bluetooth LE peripherals, call {@link android.bluetooth.BluetoothAdapter#startLeScan
+startLeScan()} on the {@link android.bluetooth.BluetoothAdapter}, passing it an implementation
+of the {@link android.bluetooth.BluetoothAdapter.LeScanCallback} interface. When the Bluetooth
+adapter detects a Bluetooth LE peripheral, your {@link
+android.bluetooth.BluetoothAdapter.LeScanCallback} implementation receives a call to the
+{@link android.bluetooth.BluetoothAdapter.LeScanCallback#onLeScan onLeScan()} method. This
+method provides you with a {@link android.bluetooth.BluetoothDevice} object representing the
+detected device, the RSSI value for the device, and a byte array containing the device's
advertisement record.</p>
-<p>If you want to scan for only specific types of peripherals, you can instead call {@link
-android.bluetooth.BluetoothAdapter#startLeScan startLeScan()} and include an array of {@link
+<p>If you want to scan for only specific types of peripherals, you can instead call {@link
+android.bluetooth.BluetoothAdapter#startLeScan startLeScan()} and include an array of {@link
java.util.UUID} objects that specify the GATT services your app supports.</p>
-<p class="note"><strong>Note:</strong> You can only scan for Bluetooth LE devices <em>or</em>
-scan for Classic Bluetooth devices using previous APIs. You cannot scan for both LE and Classic
+<p class="note"><strong>Note:</strong> You can only scan for Bluetooth LE devices <em>or</em>
+scan for Classic Bluetooth devices using previous APIs. You cannot scan for both LE and Classic
Bluetooth devices at once.</p>
-<p>To then connect to a Bluetooth LE peripheral, call {@link
-android.bluetooth.BluetoothDevice#connectGatt connectGatt()} on the corresponding
-{@link android.bluetooth.BluetoothDevice} object, passing it an implementation of
-{@link android.bluetooth.BluetoothGattCallback}. Your implementation of {@link
-android.bluetooth.BluetoothGattCallback} receives callbacks regarding the connectivity
-state with the device and other events. It's during the {@link
-android.bluetooth.BluetoothGattCallback#onConnectionStateChange onConnectionStateChange()}
-callback that you can begin communicating with the device if the method passes {@link
+<p>To then connect to a Bluetooth LE peripheral, call {@link
+android.bluetooth.BluetoothDevice#connectGatt connectGatt()} on the corresponding
+{@link android.bluetooth.BluetoothDevice} object, passing it an implementation of
+{@link android.bluetooth.BluetoothGattCallback}. Your implementation of {@link
+android.bluetooth.BluetoothGattCallback} receives callbacks regarding the connectivity
+state with the device and other events. It's during the {@link
+android.bluetooth.BluetoothGattCallback#onConnectionStateChange onConnectionStateChange()}
+callback that you can begin communicating with the device if the method passes {@link
android.bluetooth.BluetoothProfile#STATE_CONNECTED} as the new state.</p>
<p>Accessing Bluetooth features on a device also requires that your app request certain
@@ -397,39 +400,39 @@
<h3 id="WiFiScan">Wi-Fi scan-only mode</h3>
-<p>When attempting to identify the user's location, Android may use Wi-Fi to help determine
-the location by scanning nearby access points. However, users often keep Wi-Fi turned off to
-conserve battery, resulting in location data that's less accurate. Android now includes a
-scan-only mode that allows the device Wi-Fi to scan access points to help obtain the location
+<p>When attempting to identify the user's location, Android may use Wi-Fi to help determine
+the location by scanning nearby access points. However, users often keep Wi-Fi turned off to
+conserve battery, resulting in location data that's less accurate. Android now includes a
+scan-only mode that allows the device Wi-Fi to scan access points to help obtain the location
without connecting to an access point, thus greatly reducing battery usage.</p>
-<p>If you want to acquire the user's location but Wi-Fi is currently off, you can request the
-user to enable Wi-Fi scan-only mode by calling {@link android.content.Context#startActivity
-startActivity()} with the action {@link
+<p>If you want to acquire the user's location but Wi-Fi is currently off, you can request the
+user to enable Wi-Fi scan-only mode by calling {@link android.content.Context#startActivity
+startActivity()} with the action {@link
android.net.wifi.WifiManager#ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE}.</p>
<h3 id="WiFiConfig">Wi-Fi configuration</h3>
-<p>New {@link android.net.wifi.WifiEnterpriseConfig} APIs allow enterprise-oriented services to
+<p>New {@link android.net.wifi.WifiEnterpriseConfig} APIs allow enterprise-oriented services to
automate Wi-Fi configuration for managed devices.</p>
<h3 id="QuickResponse">Quick response for incoming calls</h3>
-<p>Since Android 4.0, a feature called "Quick response" allows users to respond to incoming
-calls with an immediate text message without needing to pick up the call or unlock the device.
-Until now, these quick messages were always handled by the default Messaging app. Now any app
-can declare its capability to handle these messages by creating a {@link android.app.Service}
+<p>Since Android 4.0, a feature called "Quick response" allows users to respond to incoming
+calls with an immediate text message without needing to pick up the call or unlock the device.
+Until now, these quick messages were always handled by the default Messaging app. Now any app
+can declare its capability to handle these messages by creating a {@link android.app.Service}
with an intent filter for {@link android.telephony.TelephonyManager#ACTION_RESPOND_VIA_MESSAGE}.</p>
-<p>When the user responds to an incoming call with a quick response, the Phone app sends
-the {@link android.telephony.TelephonyManager#ACTION_RESPOND_VIA_MESSAGE} intent with a URI
-describing the recipient (the caller) and the {@link android.content.Intent#EXTRA_TEXT} extra
-with the message the user wants to send. When your service receives the intent, it should deliver
+<p>When the user responds to an incoming call with a quick response, the Phone app sends
+the {@link android.telephony.TelephonyManager#ACTION_RESPOND_VIA_MESSAGE} intent with a URI
+describing the recipient (the caller) and the {@link android.content.Intent#EXTRA_TEXT} extra
+with the message the user wants to send. When your service receives the intent, it should deliver
the message and immediately stop itself (your app should not show an activity).</p>
-<p>In order to receive this intent, you must declare the {@link
+<p>In order to receive this intent, you must declare the {@link
android.Manifest.permission#SEND_RESPOND_VIA_MESSAGE} permission.</p>
@@ -438,120 +441,120 @@
<h3 id="DASH">MPEG DASH support</h3>
-<p>Android now supports Dynamic Adaptive Streaming over HTTP (DASH) in accordance with the
-ISO/IEC 23009-1 standard, using existing APIs in {@link android.media.MediaCodec} and {@link
-android.media.MediaExtractor}. The framework underlying these APIs has been updated to support
-parsing of fragmented MP4 files, but your app is still responsible for parsing the MPD metadata
+<p>Android now supports Dynamic Adaptive Streaming over HTTP (DASH) in accordance with the
+ISO/IEC 23009-1 standard, using existing APIs in {@link android.media.MediaCodec} and {@link
+android.media.MediaExtractor}. The framework underlying these APIs has been updated to support
+parsing of fragmented MP4 files, but your app is still responsible for parsing the MPD metadata
and passing the individual streams to {@link android.media.MediaExtractor}.</p>
-<p>If you want to use DASH with encrypted content, notice that the {@link android.media.MediaExtractor#getSampleCryptoInfo getSampleCryptoInfo()} method returns the {@link
-android.media.MediaCodec.CryptoInfo} metadata describing the structure of each encrypted media
-sample. Also, the {@link android.media.MediaExtractor#getPsshInfo()} method has been added to
-{@link android.media.MediaExtractor} so you can access the PSSH metadata for your DASH media.
-This method returns a map of {@link java.util.UUID} objects to bytes, with the
-{@link java.util.UUID} specifying the crypto scheme, and the bytes being the data specific
+<p>If you want to use DASH with encrypted content, notice that the {@link android.media.MediaExtractor#getSampleCryptoInfo getSampleCryptoInfo()} method returns the {@link
+android.media.MediaCodec.CryptoInfo} metadata describing the structure of each encrypted media
+sample. Also, the {@link android.media.MediaExtractor#getPsshInfo()} method has been added to
+{@link android.media.MediaExtractor} so you can access the PSSH metadata for your DASH media.
+This method returns a map of {@link java.util.UUID} objects to bytes, with the
+{@link java.util.UUID} specifying the crypto scheme, and the bytes being the data specific
to that scheme.</p>
<h3 id="DRM">Media DRM</h3>
<p>The new {@link android.media.MediaDrm} class provides a modular solution for digital rights
-management (DRM) with your media content by separating DRM concerns from media playback. For
-instance, this API separation allows you to play back Widevine-encrypted content without having
-to use the Widevine media format. This DRM solution also supports DASH Common Encryption so you
+management (DRM) with your media content by separating DRM concerns from media playback. For
+instance, this API separation allows you to play back Widevine-encrypted content without having
+to use the Widevine media format. This DRM solution also supports DASH Common Encryption so you
can use a variety of DRM schemes with your streaming content.</p>
-<p>You can use {@link android.media.MediaDrm} to obtain opaque key-request messages and process
-key-response messages from the server for license acquisition and provisioning. Your app is
-responsible for handling the network communication with the servers; the {@link
+<p>You can use {@link android.media.MediaDrm} to obtain opaque key-request messages and process
+key-response messages from the server for license acquisition and provisioning. Your app is
+responsible for handling the network communication with the servers; the {@link
android.media.MediaDrm} class provides only the ability to generate and process the messages.</p>
-<p>The {@link android.media.MediaDrm} APIs are intended to be used in conjunction with the
-{@link android.media.MediaCodec} APIs that were introduced in Android 4.1 (API level 16),
-including {@link android.media.MediaCodec} for encoding and decoding your content, {@link
-android.media.MediaCrypto} for handling encrypted content, and {@link android.media.MediaExtractor}
+<p>The {@link android.media.MediaDrm} APIs are intended to be used in conjunction with the
+{@link android.media.MediaCodec} APIs that were introduced in Android 4.1 (API level 16),
+including {@link android.media.MediaCodec} for encoding and decoding your content, {@link
+android.media.MediaCrypto} for handling encrypted content, and {@link android.media.MediaExtractor}
for extracting and demuxing your content.</p>
-<p>You must first construct {@link android.media.MediaExtractor} and
-{@link android.media.MediaCodec} objects. You can then access the DRM-scheme-identifying
-{@link java.util.UUID}, typically from metadata in the content, and use it to construct an
+<p>You must first construct {@link android.media.MediaExtractor} and
+{@link android.media.MediaCodec} objects. You can then access the DRM-scheme-identifying
+{@link java.util.UUID}, typically from metadata in the content, and use it to construct an
instance of a {@link android.media.MediaDrm} object with its constructor.</p>
<h3 id="EncodingSurface">Video encoding from a Surface</h3>
-<p>Android 4.1 (API level 16) added the {@link android.media.MediaCodec} class for low-level
-encoding and decoding of media content. When encoding video, Android 4.1 required that you provide
-the media with a {@link java.nio.ByteBuffer} array, but Android 4.3 now allows you to use a {@link
-android.view.Surface} as the input to an encoder. For instance, this allows you to encode input
+<p>Android 4.1 (API level 16) added the {@link android.media.MediaCodec} class for low-level
+encoding and decoding of media content. When encoding video, Android 4.1 required that you provide
+the media with a {@link java.nio.ByteBuffer} array, but Android 4.3 now allows you to use a {@link
+android.view.Surface} as the input to an encoder. For instance, this allows you to encode input
from an existing video file or using frames generated from OpenGL ES.</p>
-<p>To use a {@link android.view.Surface} as the input to your encoder, first call {@link
-android.media.MediaCodec#configure configure()} for your {@link android.media.MediaCodec}.
-Then call {@link android.media.MediaCodec#createInputSurface()} to receive the {@link
+<p>To use a {@link android.view.Surface} as the input to your encoder, first call {@link
+android.media.MediaCodec#configure configure()} for your {@link android.media.MediaCodec}.
+Then call {@link android.media.MediaCodec#createInputSurface()} to receive the {@link
android.view.Surface} upon which you can stream your media.</p>
-<p>For example, you can use the given {@link android.view.Surface} as the window for an OpenGL
-context by passing it to {@link android.opengl.EGL14#eglCreateWindowSurface
-eglCreateWindowSurface()}. Then while rendering the surface, call {@link
-android.opengl.EGL14#eglSwapBuffers eglSwapBuffers()} to pass the frame to the {@link
+<p>For example, you can use the given {@link android.view.Surface} as the window for an OpenGL
+context by passing it to {@link android.opengl.EGL14#eglCreateWindowSurface
+eglCreateWindowSurface()}. Then while rendering the surface, call {@link
+android.opengl.EGL14#eglSwapBuffers eglSwapBuffers()} to pass the frame to the {@link
android.media.MediaCodec}.</p>
-<p>To begin encoding, call {@link android.media.MediaCodec#start()} on the {@link
-android.media.MediaCodec}. When done, call {@link android.media.MediaCodec#signalEndOfInputStream}
-to terminate encoding, and call {@link android.view.Surface#release()} on the
+<p>To begin encoding, call {@link android.media.MediaCodec#start()} on the {@link
+android.media.MediaCodec}. When done, call {@link android.media.MediaCodec#signalEndOfInputStream}
+to terminate encoding, and call {@link android.view.Surface#release()} on the
{@link android.view.Surface}.</p>
<h3 id="MediaMuxing">Media muxing</h3>
-<p>The new {@link android.media.MediaMuxer} class enables multiplexing between one audio stream
-and one video stream. These APIs serve as a counterpart to the {@link android.media.MediaExtractor}
+<p>The new {@link android.media.MediaMuxer} class enables multiplexing between one audio stream
+and one video stream. These APIs serve as a counterpart to the {@link android.media.MediaExtractor}
class added in Android 4.2 for de-multiplexing (demuxing) media.</p>
-<p>Supported output formats are defined in {@link android.media.MediaMuxer.OutputFormat}. Currently,
-MP4 is the only supported output format and {@link android.media.MediaMuxer} currently supports
+<p>Supported output formats are defined in {@link android.media.MediaMuxer.OutputFormat}. Currently,
+MP4 is the only supported output format and {@link android.media.MediaMuxer} currently supports
only one audio stream and/or one video stream at a time.</p>
-<p>{@link android.media.MediaMuxer} is mostly designed to work with {@link android.media.MediaCodec}
-so you can perform video processing through {@link android.media.MediaCodec} then save the
-output to an MP4 file through {@link android.media.MediaMuxer}. You can also use {@link
-android.media.MediaMuxer} in combination with {@link android.media.MediaExtractor} to perform
+<p>{@link android.media.MediaMuxer} is mostly designed to work with {@link android.media.MediaCodec}
+so you can perform video processing through {@link android.media.MediaCodec} then save the
+output to an MP4 file through {@link android.media.MediaMuxer}. You can also use {@link
+android.media.MediaMuxer} in combination with {@link android.media.MediaExtractor} to perform
media editing without the need to encode or decode.</p>
<h3 id="ProgressAndScrubbing">Playback progress and scrubbing for RemoteControlClient</h3>
-<p>In Android 4.0 (API level 14), the {@link android.media.RemoteControlClient} was added to
-enable media playback controls from remote control clients such as the controls available on the
-lock screen. Android 4.3 now provides the ability for such controllers to display the playback
-position and controls for scrubbing the playback. If you've enabled remote control for your
-media app with the {@link android.media.RemoteControlClient} APIs, then you can allow playback
+<p>In Android 4.0 (API level 14), the {@link android.media.RemoteControlClient} was added to
+enable media playback controls from remote control clients such as the controls available on the
+lock screen. Android 4.3 now provides the ability for such controllers to display the playback
+position and controls for scrubbing the playback. If you've enabled remote control for your
+media app with the {@link android.media.RemoteControlClient} APIs, then you can allow playback
scrubbing by implementing two new interfaces.</p>
-<p>First, you must enable the {@link
-android.media.RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE} flag by passing it to
-{@link android.media.RemoteControlClient#setTransportControlFlags setTransportControlsFlags()}.</p>
+<p>First, you must enable the {@link
+android.media.RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE} flag by passing it to
+{@link android.media.RemoteControlClient#setTransportControlFlags setTransportControlsFlags()}.</p>
<p>Then implement the following two new interfaces:</p>
<dl>
<dt>{@link android.media.RemoteControlClient.OnGetPlaybackPositionListener}</dt>
- <dd>This includes the callback {@link android.media.RemoteControlClient.OnGetPlaybackPositionListener#onGetPlaybackPosition}, which requests the current position
+ <dd>This includes the callback {@link android.media.RemoteControlClient.OnGetPlaybackPositionListener#onGetPlaybackPosition}, which requests the current position
of your media when the remote control needs to update the progress in its UI.</dd>
<dt>{@link android.media.RemoteControlClient.OnPlaybackPositionUpdateListener}</dt>
- <dd>This includes the callback {@link android.media.RemoteControlClient.OnPlaybackPositionUpdateListener#onPlaybackPositionUpdate onPlaybackPositionUpdate()}, which
- tells your app the new time code for your media when the user scrubs the playback with the
+ <dd>This includes the callback {@link android.media.RemoteControlClient.OnPlaybackPositionUpdateListener#onPlaybackPositionUpdate onPlaybackPositionUpdate()}, which
+ tells your app the new time code for your media when the user scrubs the playback with the
remote control UI.
- <p>Once you update your playback with the new position, call {@link
- android.media.RemoteControlClient#setPlaybackState setPlaybackState()} to indicate the
+ <p>Once you update your playback with the new position, call {@link
+ android.media.RemoteControlClient#setPlaybackState setPlaybackState()} to indicate the
new playback state, position, and speed.</p>
</dd>
</dl>
-<p>With these interfaces defined, you can set them for your {@link
-android.media.RemoteControlClient} by calling {@link android.media.RemoteControlClient#setOnGetPlaybackPositionListener setOnGetPlaybackPositionListener()} and
-{@link android.media.RemoteControlClient#setPlaybackPositionUpdateListener
+<p>With these interfaces defined, you can set them for your {@link
+android.media.RemoteControlClient} by calling {@link android.media.RemoteControlClient#setOnGetPlaybackPositionListener setOnGetPlaybackPositionListener()} and
+{@link android.media.RemoteControlClient#setPlaybackPositionUpdateListener
setPlaybackPositionUpdateListener()}, respectively.</p>
@@ -560,7 +563,7 @@
<h3 id="OpenGL">Support for OpenGL ES 3.0</h3>
-<p>Android 4.3 adds Java interfaces and native support for OpenGL ES 3.0. Key new functionality
+<p>Android 4.3 adds Java interfaces and native support for OpenGL ES 3.0. Key new functionality
provided in OpenGL ES 3.0 includes:</p>
<ul>
<li>Acceleration of advanced visual effects</li>
@@ -570,8 +573,8 @@
<li>Broader standardization of texture size and render-buffer formats</li>
</ul>
-<p>The Java interface for OpenGL ES 3.0 on Android is provided with {@link android.opengl.GLES30}.
-When using OpenGL ES 3.0, be sure that you declare it in your manifest file with the
+<p>The Java interface for OpenGL ES 3.0 on Android is provided with {@link android.opengl.GLES30}.
+When using OpenGL ES 3.0, be sure that you declare it in your manifest file with the
<a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><uses-feature></a>
tag and the {@code android:glEsVersion} attribute. For example:</p>
<pre>
@@ -586,16 +589,16 @@
<h3 id="MipMap">Mipmapping for drawables</h3>
-<p>Using a mipmap as the source for your bitmap or drawable is a simple way to provide a
-quality image and various image scales, which can be particularly useful if you expect your
+<p>Using a mipmap as the source for your bitmap or drawable is a simple way to provide a
+quality image and various image scales, which can be particularly useful if you expect your
image to be scaled during an animation.</p>
-<p>Android 4.2 (API level 17) added support for mipmaps in the {@link android.graphics.Bitmap}
-class—Android swaps the mip images in your {@link android.graphics.Bitmap} when you've
-supplied a mipmap source and have enabled {@link android.graphics.Bitmap#setHasMipMap
-setHasMipMap()}. Now in Android 4.3, you can enable mipmaps for a {@link
-android.graphics.drawable.BitmapDrawable} object as well, by providing a mipmap asset and
-setting the {@code android:mipMap} attribute in a bitmap resource file or by calling {@link
+<p>Android 4.2 (API level 17) added support for mipmaps in the {@link android.graphics.Bitmap}
+class—Android swaps the mip images in your {@link android.graphics.Bitmap} when you've
+supplied a mipmap source and have enabled {@link android.graphics.Bitmap#setHasMipMap
+setHasMipMap()}. Now in Android 4.3, you can enable mipmaps for a {@link
+android.graphics.drawable.BitmapDrawable} object as well, by providing a mipmap asset and
+setting the {@code android:mipMap} attribute in a bitmap resource file or by calling {@link
android.graphics.drawable.BitmapDrawable#hasMipMap hasMipMap()}.
</p>
@@ -605,36 +608,36 @@
<h3 id="ViewOverlay">View overlays</h3>
-<p>The new {@link android.view.ViewOverlay} class provides a transparent layer on top of
-a {@link android.view.View} on which you can add visual content and which does not affect
-the layout hierarchy. You can get a {@link android.view.ViewOverlay} for any {@link
-android.view.View} by calling {@link android.view.View#getOverlay}. The overlay
-always has the same size and position as its host view (the view from which it was created),
-allowing you to add content that appears in front of the host view, but which cannot extend
+<p>The new {@link android.view.ViewOverlay} class provides a transparent layer on top of
+a {@link android.view.View} on which you can add visual content and which does not affect
+the layout hierarchy. You can get a {@link android.view.ViewOverlay} for any {@link
+android.view.View} by calling {@link android.view.View#getOverlay}. The overlay
+always has the same size and position as its host view (the view from which it was created),
+allowing you to add content that appears in front of the host view, but which cannot extend
the bounds of that host view.
</p>
-<p>Using a {@link android.view.ViewOverlay} is particularly useful when you want to create
-animations such as sliding a view outside of its container or moving items around the screen
-without affecting the view hierarchy. However, because the usable area of an overlay is
-restricted to the same area as its host view, if you want to animate a view moving outside
-its position in the layout, you must use an overlay from a parent view that has the desired
+<p>Using a {@link android.view.ViewOverlay} is particularly useful when you want to create
+animations such as sliding a view outside of its container or moving items around the screen
+without affecting the view hierarchy. However, because the usable area of an overlay is
+restricted to the same area as its host view, if you want to animate a view moving outside
+its position in the layout, you must use an overlay from a parent view that has the desired
layout bounds.</p>
-<p>When you create an overlay for a widget view such as a {@link android.widget.Button}, you
-can add {@link android.graphics.drawable.Drawable} objects to the overlay by calling
-{@link android.view.ViewOverlay#add(Drawable)}. If you call {@link
+<p>When you create an overlay for a widget view such as a {@link android.widget.Button}, you
+can add {@link android.graphics.drawable.Drawable} objects to the overlay by calling
+{@link android.view.ViewOverlay#add(Drawable)}. If you call {@link
android.view.ViewGroup#getOverlay} for a layout view, such as {@link android.widget.RelativeLayout},
the object returned is a {@link android.view.ViewGroupOverlay}. The
-{@link android.view.ViewGroupOverlay} class is a subclass
-of {@link android.view.ViewOverlay} that also allows you to add {@link android.view.View}
+{@link android.view.ViewGroupOverlay} class is a subclass
+of {@link android.view.ViewOverlay} that also allows you to add {@link android.view.View}
objects by calling {@link android.view.ViewGroupOverlay#add(View)}.
</p>
-<p class="note"><strong>Note:</strong> All drawables and views that you add to an overlay
+<p class="note"><strong>Note:</strong> All drawables and views that you add to an overlay
are visual only. They cannot receive focus or input events.</p>
-<p>For example, the following code animates a view sliding to the right by placing the view
+<p>For example, the following code animates a view sliding to the right by placing the view
in the parent view's overlay, then performing a translation animation on that view:</p>
<pre>
View view = findViewById(R.id.view_to_remove);
@@ -647,17 +650,17 @@
<h3 id="OpticalBounds">Optical bounds layout</h3>
-<p>For views that contain nine-patch background images, you can now specify that they should
-be aligned with neighboring views based on the "optical" bounds of the background image rather
+<p>For views that contain nine-patch background images, you can now specify that they should
+be aligned with neighboring views based on the "optical" bounds of the background image rather
than the "clip" bounds of the view.</p>
-<p>For example, figures 1 and 2 each show the same layout, but the version in figure 1 is
-using clip bounds (the default behavior), while figure 2 is using optical bounds. Because the
-nine-patch images used for the button and the photo frame include padding around the edges,
+<p>For example, figures 1 and 2 each show the same layout, but the version in figure 1 is
+using clip bounds (the default behavior), while figure 2 is using optical bounds. Because the
+nine-patch images used for the button and the photo frame include padding around the edges,
they don’t appear to align with each other or the text when using clip bounds.</p>
-<p class="note"><strong>Note:</strong> The screenshot in figures 1 and 2 have the "Show
-layout bounds" developer setting enabled. For each view, red lines indicate the optical
+<p class="note"><strong>Note:</strong> The screenshot in figures 1 and 2 have the "Show
+layout bounds" developer setting enabled. For each view, red lines indicate the optical
bounds, blue lines indicate the clip bounds, and pink indicates margins.</p>
<script type="text/javascript">
@@ -725,30 +728,30 @@
</p>
</div>
-<p>For this to work, the nine-patch images applied to the background of your views must specify
-the optical bounds using red lines along the bottom and right-side of the nine-patch file (as
-shown in figure 3). The red lines indicate the region that should be subtracted from
+<p>For this to work, the nine-patch images applied to the background of your views must specify
+the optical bounds using red lines along the bottom and right-side of the nine-patch file (as
+shown in figure 3). The red lines indicate the region that should be subtracted from
the clip bounds, leaving the optical bounds of the image.</p>
-<p>When you enable optical bounds for a {@link android.view.ViewGroup} in your layout, all
-descendant views inherit the optical bounds layout mode unless you override it for a group by
-setting {@code android:layoutMode} to {@code "clipBounds"}. All layout elements also honor the
-optical bounds of their child views, adapting their own bounds based on the optical bounds of
-the views within them. However, layout elements (subclasses of {@link android.view.ViewGroup})
+<p>When you enable optical bounds for a {@link android.view.ViewGroup} in your layout, all
+descendant views inherit the optical bounds layout mode unless you override it for a group by
+setting {@code android:layoutMode} to {@code "clipBounds"}. All layout elements also honor the
+optical bounds of their child views, adapting their own bounds based on the optical bounds of
+the views within them. However, layout elements (subclasses of {@link android.view.ViewGroup})
currently do not support optical bounds for nine-patch images applied to their own background.</p>
<p>If you create a custom view by subclassing {@link android.view.View}, {@link android.view.ViewGroup}, or any subclasses thereof, your view will inherit these optical bound behaviors.</p>
<p class="note"><strong>Note:</strong> All widgets supported by the Holo theme have been updated
-with optical bounds, including {@link android.widget.Button}, {@link android.widget.Spinner},
+with optical bounds, including {@link android.widget.Button}, {@link android.widget.Spinner},
{@link android.widget.EditText}, and others. So you can immediately benefit by setting the
-{@code android:layoutMode} attribute to {@code "opticalBounds"} if your app applies a Holo theme
-({@link android.R.style#Theme_Holo Theme.Holo}, {@link android.R.style#Theme_Holo_Light
+{@code android:layoutMode} attribute to {@code "opticalBounds"} if your app applies a Holo theme
+({@link android.R.style#Theme_Holo Theme.Holo}, {@link android.R.style#Theme_Holo_Light
Theme.Holo.Light}, etc.).
</p>
-<p>To specify optical bounds for your own nine-patch images with the <a
-href="{@docRoot}tools/help/draw9patch.html">Draw 9-patch</a> tool, hold CTRL when clicking on
+<p>To specify optical bounds for your own nine-patch images with the <a
+href="{@docRoot}tools/help/draw9patch.html">Draw 9-patch</a> tool, hold CTRL when clicking on
the border pixels.</p>
@@ -756,59 +759,59 @@
<h3 id="AnimationRect">Animation for Rect values</h3>
-<p>You can now animate between two {@link android.graphics.Rect} values with the new {@link
-android.animation.RectEvaluator}. This new class is an implementation of {@link
-android.animation.TypeEvaluator} that you can pass to {@link
+<p>You can now animate between two {@link android.graphics.Rect} values with the new {@link
+android.animation.RectEvaluator}. This new class is an implementation of {@link
+android.animation.TypeEvaluator} that you can pass to {@link
android.animation.ValueAnimator#setEvaluator ValueAnimator.setEvaluator()}.
</p>
<h3 id="AttachFocus">Window attach and focus listener</h3>
-<p>Previously, if you wanted to listen for when your view attached/detached to the window or
-when its focus changed, you needed to override the {@link android.view.View} class to
-implement {@link android.view.View#onAttachedToWindow onAttachedToWindow()} and {@link
-android.view.View#onDetachedFromWindow onDetachedFromWindow()}, or {@link
+<p>Previously, if you wanted to listen for when your view attached/detached to the window or
+when its focus changed, you needed to override the {@link android.view.View} class to
+implement {@link android.view.View#onAttachedToWindow onAttachedToWindow()} and {@link
+android.view.View#onDetachedFromWindow onDetachedFromWindow()}, or {@link
android.view.View#onWindowFocusChanged onWindowFocusChanged()}, respectively.
</p>
-<p>Now, to receive attach and detach events you can instead implement {@link
-android.view.ViewTreeObserver.OnWindowAttachListener} and set it on a view with
-{@link android.view.ViewTreeObserver#addOnWindowAttachListener addOnWindowAttachListener()}.
-And to receive focus events, you can implement {@link
-android.view.ViewTreeObserver.OnWindowFocusChangeListener} and set it on a view with
-{@link android.view.ViewTreeObserver#addOnWindowFocusChangeListener
+<p>Now, to receive attach and detach events you can instead implement {@link
+android.view.ViewTreeObserver.OnWindowAttachListener} and set it on a view with
+{@link android.view.ViewTreeObserver#addOnWindowAttachListener addOnWindowAttachListener()}.
+And to receive focus events, you can implement {@link
+android.view.ViewTreeObserver.OnWindowFocusChangeListener} and set it on a view with
+{@link android.view.ViewTreeObserver#addOnWindowFocusChangeListener
addOnWindowFocusChangeListener()}.
</p>
<h3 id="Overscan">TV overscan support</h3>
-<p>To be sure your app fills the entire screen on every television, you can now enable overscan
-for you app layout. Overscan mode is determined by the {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag, which you can enable with platform themes such as
-{@link android.R.style#Theme_DeviceDefault_NoActionBar_Overscan} or by enabling the
+<p>To be sure your app fills the entire screen on every television, you can now enable overscan
+for you app layout. Overscan mode is determined by the {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag, which you can enable with platform themes such as
+{@link android.R.style#Theme_DeviceDefault_NoActionBar_Overscan} or by enabling the
{@link android.R.attr#windowOverscan} style in a custom theme.</p>
<h3 id="Orientation">Screen orientation</h3>
-<p>The <a
+<p>The <a
href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>
-tag's <a
+tag's <a
href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code screenOrientation}</a>
attribute now supports additional values to honor the user's preference for auto-rotation:</p>
<dl>
<dt>{@code "userLandscape"}</dt>
-<dd>Behaves the same as {@code "sensorLandscape"}, except if the user disables auto-rotate
+<dd>Behaves the same as {@code "sensorLandscape"}, except if the user disables auto-rotate
then it locks in the normal landscape orientation and will not flip.
</dd>
<dt>{@code "userPortrait"}</dt>
-<dd>Behaves the same as {@code "sensorPortrait"}, except if the user disables auto-rotate then
+<dd>Behaves the same as {@code "sensorPortrait"}, except if the user disables auto-rotate then
it locks in the normal portrait orientation and will not flip.
</dd>
<dt>{@code "fullUser"}</dt>
-<dd>Behaves the same as {@code "fullSensor"} and allows rotation in all four directions, except
+<dd>Behaves the same as {@code "fullSensor"} and allows rotation in all four directions, except
if the user disables auto-rotate then it locks in the user's preferred orientation.
</dd></dl>
@@ -818,8 +821,8 @@
<h3 id="RotationAnimation">Rotation animations</h3>
-<p>The new {@link android.view.WindowManager.LayoutParams#rotationAnimation} field in
-{@link android.view.WindowManager} allows you to select between one of three animations you
+<p>The new {@link android.view.WindowManager.LayoutParams#rotationAnimation} field in
+{@link android.view.WindowManager} allows you to select between one of three animations you
want to use when the system switches screen orientations. The three animations are:</p>
<ul>
<li>{@link android.view.WindowManager.LayoutParams#ROTATION_ANIMATION_CROSSFADE}</li>
@@ -844,25 +847,17 @@
<h2 id="UserInput">User Input</h2>
-<h3 id="SignificantMotion">Detect significant motion</h3>
-
-<p>The {@link android.hardware.SensorManager} APIs now allow you to request a callback when the
-device sensors detect "significant motion." For instance, this event may be triggered by new
-motion such as when the user starts to walk.</p>
-
-<p>To register a listener for significant motion, extend the {@link android.hardware.TriggerEventListener} class and implement the {@link android.hardware.TriggerEventListener#onTrigger onTrigger()} callback method. Then register your event listener with the {@link android.hardware.SensorManager} by passing it to {@link android.hardware.SensorManager#requestTriggerSensor requestTriggerSensor()}, passing it your {@link android.hardware.TriggerEventListener} and {@link android.hardware.Sensor#TYPE_SIGNIFICANT_MOTION}.</p>
-
<h3 id="Sensors">New sensor types</h3>
<p>The new {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR} sensor allows you to detect the device's rotations without worrying about magnetic interferences. Unlike the {@link android.hardware.Sensor#TYPE_ROTATION_VECTOR} sensor, the {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR} is not based on magnetic north.</p>
-<p>The new {@link android.hardware.Sensor#TYPE_GYROSCOPE_UNCALIBRATED} and {@link
-android.hardware.Sensor#TYPE_MAGNETIC_FIELD_UNCALIBRATED} sensors provide raw sensor data without
-consideration for bias estimations. That is, the existing {@link
-android.hardware.Sensor#TYPE_GYROSCOPE} and {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD}
-sensors provide sensor data that takes into account estimated bias from gyro-drift and hard iron
-in the device, respectively. Whereas the new "uncalibrated" versions of these sensors instead provide
-the raw sensor data and offer the estimated bias values separately. These sensors allow you to
-provide your own custom calibration for the sensor data by enhancing the estimated bias with
+<p>The new {@link android.hardware.Sensor#TYPE_GYROSCOPE_UNCALIBRATED} and {@link
+android.hardware.Sensor#TYPE_MAGNETIC_FIELD_UNCALIBRATED} sensors provide raw sensor data without
+consideration for bias estimations. That is, the existing {@link
+android.hardware.Sensor#TYPE_GYROSCOPE} and {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD}
+sensors provide sensor data that takes into account estimated bias from gyro-drift and hard iron
+in the device, respectively. Whereas the new "uncalibrated" versions of these sensors instead provide
+the raw sensor data and offer the estimated bias values separately. These sensors allow you to
+provide your own custom calibration for the sensor data by enhancing the estimated bias with
external data.</p>
@@ -891,13 +886,13 @@
<p>To track which contacts have been deleted, the new table {@link android.provider.ContactsContract.DeletedContacts} provides a log of contacts that have been deleted (but each contact deleted is held in this table for a limited time). Similar to {@link android.provider.ContactsContract.ContactsColumns#CONTACT_LAST_UPDATED_TIMESTAMP}, you can use the new selection parameter, {@link android.provider.ContactsContract.DeletedContacts#CONTACT_DELETED_TIMESTAMP} to check which contacts have been deleted since the last time you queried the provider. The table also contains the constant {@link android.provider.ContactsContract.DeletedContacts#DAYS_KEPT_MILLISECONDS} containing the number of days (in milliseconds) that the log will be kept.</p>
-<p>Additionally, the Contacts Provider now broadcasts the {@link
-android.provider.ContactsContract.Intents#CONTACTS_DATABASE_CREATED} action when the user
-clears the contacts storage through the system settings menu, effectively recreating the
-Contacts Provider database. It’s intended to signal apps that they need to drop all the contact
+<p>Additionally, the Contacts Provider now broadcasts the {@link
+android.provider.ContactsContract.Intents#CONTACTS_DATABASE_CREATED} action when the user
+clears the contacts storage through the system settings menu, effectively recreating the
+Contacts Provider database. It’s intended to signal apps that they need to drop all the contact
information they’ve stored and reload it with a new query.</p>
-<p>For sample code using these APIs to check for changes to the contacts, look in the ApiDemos
+<p>For sample code using these APIs to check for changes to the contacts, look in the ApiDemos
sample available in the <a href="{@docRoot}tools/samples/index.html">SDK Samples</a> download.</p>
@@ -905,13 +900,13 @@
<h3 id="BiDi">Improved support for bi-directional text</h3>
-<p>Previous versions of Android support right-to-left (RTL) languages and layout,
-but sometimes don't properly handle mixed-direction text. So Android 4.3 adds the {@link
-android.text.BidiFormatter} APIs that help you properly format text with opposite-direction
+<p>Previous versions of Android support right-to-left (RTL) languages and layout,
+but sometimes don't properly handle mixed-direction text. So Android 4.3 adds the {@link
+android.text.BidiFormatter} APIs that help you properly format text with opposite-direction
content without garbling any parts of it.</p>
-<p>For example, when you want to create a sentence with a string variable, such as "Did you mean
-15 Bay Street, Laurel, CA?", you normally pass a localized string resource and the variable to
+<p>For example, when you want to create a sentence with a string variable, such as "Did you mean
+15 Bay Street, Laurel, CA?", you normally pass a localized string resource and the variable to
{@link java.lang.String#format String.format()}:</p>
<pre>
Resources res = getResources();
@@ -922,8 +917,8 @@
<p dir="rtl">האם התכוונת ל 15 Bay Street, Laurel, CA?</p>
-<p>That's wrong because the "15" should be left of "Bay Street." The solution is to use {@link
-android.text.BidiFormatter} and its {@link android.text.BidiFormatter#unicodeWrap(String)
+<p>That's wrong because the "15" should be left of "Bay Street." The solution is to use {@link
+android.text.BidiFormatter} and its {@link android.text.BidiFormatter#unicodeWrap(String)
unicodeWrap()} method. For example, the code above becomes:</p>
<pre>
Resources res = getResources();
@@ -933,11 +928,11 @@
</pre>
<p>
-By default, {@link android.text.BidiFormatter#unicodeWrap(String) unicodeWrap()} uses the
-first-strong directionality estimation heuristic, which can get things wrong if the first
-signal for text direction does not represent the appropriate direction for the content as a whole.
-If necessary, you can specify a different heuristic by passing one of the {@link
-android.text.TextDirectionHeuristic} constants from {@link android.text.TextDirectionHeuristics}
+By default, {@link android.text.BidiFormatter#unicodeWrap(String) unicodeWrap()} uses the
+first-strong directionality estimation heuristic, which can get things wrong if the first
+signal for text direction does not represent the appropriate direction for the content as a whole.
+If necessary, you can specify a different heuristic by passing one of the {@link
+android.text.TextDirectionHeuristic} constants from {@link android.text.TextDirectionHeuristics}
to {@link android.text.BidiFormatter#unicodeWrap(String,TextDirectionHeuristic) unicodeWrap()}.</p>
<p class="note"><strong>Note:</strong> These new APIs are also available for previous versions
@@ -950,31 +945,31 @@
<h3 id="A11yKeyEvents">Handle key events</h3>
-<p>An {@link android.accessibilityservice.AccessibilityService} can now receive a callback for
-key input events with the {@link android.accessibilityservice.AccessibilityService#onKeyEvent
-onKeyEvent()} callback method. This allows your accessibility service to handle input for
-key-based input devices such as a keyboard and translate those events to special actions that
+<p>An {@link android.accessibilityservice.AccessibilityService} can now receive a callback for
+key input events with the {@link android.accessibilityservice.AccessibilityService#onKeyEvent
+onKeyEvent()} callback method. This allows your accessibility service to handle input for
+key-based input devices such as a keyboard and translate those events to special actions that
previously may have been possible only with touch input or the device's directional pad.</p>
<h3 id="A11yText">Select text and copy/paste</h3>
-<p>The {@link android.view.accessibility.AccessibilityNodeInfo} now provides APIs that allow
-an {@link android.accessibilityservice.AccessibilityService} to select, cut, copy, and paste
+<p>The {@link android.view.accessibility.AccessibilityNodeInfo} now provides APIs that allow
+an {@link android.accessibilityservice.AccessibilityService} to select, cut, copy, and paste
text in a node.</p>
-<p>To specify the selection of text to cut or copy, your accessibility service can use the new
-action, {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_SET_SELECTION}, passing
-with it the selection start and end position with {@link
-android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT} and {@link
-android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT}.
-Alternatively you can select text by manipulating the cursor position using the existing
-action, {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}
-(previously only for moving the cursor position), and adding the argument {@link
+<p>To specify the selection of text to cut or copy, your accessibility service can use the new
+action, {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_SET_SELECTION}, passing
+with it the selection start and end position with {@link
+android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT} and {@link
+android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT}.
+Alternatively you can select text by manipulating the cursor position using the existing
+action, {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}
+(previously only for moving the cursor position), and adding the argument {@link
android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}.</p>
-<p>You can then cut or copy with {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_CUT},
-{@link android.view.accessibility.AccessibilityNodeInfo#ACTION_COPY}, then later paste with
+<p>You can then cut or copy with {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_CUT},
+{@link android.view.accessibility.AccessibilityNodeInfo#ACTION_COPY}, then later paste with
{@link android.view.accessibility.AccessibilityNodeInfo#ACTION_PASTE}.</p>
@@ -987,14 +982,14 @@
<h3 id="A11yFeatures">Declare accessibility features</h3>
-<p>Beginning with Android 4.3, an accessibility service must declare accessibility capabilities
-in its metadata file in order to use certain accessibility features. If the capability is not
-requested in the metadata file, then the feature will be a no-op. To declare your service's
-accessibility capabilities, you must use XML attributes that correspond to the various
-"capability" constants in the {@link android.accessibilityservice.AccessibilityServiceInfo}
+<p>Beginning with Android 4.3, an accessibility service must declare accessibility capabilities
+in its metadata file in order to use certain accessibility features. If the capability is not
+requested in the metadata file, then the feature will be a no-op. To declare your service's
+accessibility capabilities, you must use XML attributes that correspond to the various
+"capability" constants in the {@link android.accessibilityservice.AccessibilityServiceInfo}
class.</p>
-<p>For example, if a service does not request the {@link android.R.styleable#AccessibilityService_canRequestFilterKeyEvents flagRequestFilterKeyEvents} capability,
+<p>For example, if a service does not request the {@link android.R.styleable#AccessibilityService_canRequestFilterKeyEvents flagRequestFilterKeyEvents} capability,
then it will not receive key events.</p>
@@ -1002,36 +997,36 @@
<h3 id="UiAutomation">Automated UI testing</h3>
-<p>The new {@link android.app.UiAutomation} class provides APIs that allow you to simulate user
-actions for test automation. By using the platform's {@link
-android.accessibilityservice.AccessibilityService} APIs, the {@link android.app.UiAutomation}
+<p>The new {@link android.app.UiAutomation} class provides APIs that allow you to simulate user
+actions for test automation. By using the platform's {@link
+android.accessibilityservice.AccessibilityService} APIs, the {@link android.app.UiAutomation}
APIs allow you to inspect the screen content and inject arbitrary keyboard and touch events.</p>
-<p>To get an instance of {@link android.app.UiAutomation}, call {@link
-android.app.Instrumentation#getUiAutomation Instrumentation.getUiAutomation()}. In order
-for this to work, you must supply the {@code -w} option with the {@code instrument} command
-when running your {@link android.test.InstrumentationTestCase} from <a
+<p>To get an instance of {@link android.app.UiAutomation}, call {@link
+android.app.Instrumentation#getUiAutomation Instrumentation.getUiAutomation()}. In order
+for this to work, you must supply the {@code -w} option with the {@code instrument} command
+when running your {@link android.test.InstrumentationTestCase} from <a
href="{@docRoot}tools/help/adb.html#am">{@code adb shell}</a>.</p>
-<p>With the {@link android.app.UiAutomation} instance, you can execute arbitrary events to test
-your app by calling {@link android.app.UiAutomation#executeAndWaitForEvent
-executeAndWaitForEvent()}, passing it a {@link java.lang.Runnable} to perform, a timeout
-period for the operation, and an implementation of the {@link
-android.app.UiAutomation.AccessibilityEventFilter} interface. It's within your {@link
-android.app.UiAutomation.AccessibilityEventFilter} implementation that you'll receive a call
-that allows you to filter the events that you're interested in and determine the success or
+<p>With the {@link android.app.UiAutomation} instance, you can execute arbitrary events to test
+your app by calling {@link android.app.UiAutomation#executeAndWaitForEvent
+executeAndWaitForEvent()}, passing it a {@link java.lang.Runnable} to perform, a timeout
+period for the operation, and an implementation of the {@link
+android.app.UiAutomation.AccessibilityEventFilter} interface. It's within your {@link
+android.app.UiAutomation.AccessibilityEventFilter} implementation that you'll receive a call
+that allows you to filter the events that you're interested in and determine the success or
failure of a given test case.</p>
-<p>To observe all the events during a test, create an implementation of {@link
-android.app.UiAutomation.OnAccessibilityEventListener} and pass it to {@link
-android.app.UiAutomation#setOnAccessibilityEventListener setOnAccessibilityEventListener()}.
-Your listener interface then receives a call to {@link
-android.app.UiAutomation.OnAccessibilityEventListener#onAccessibilityEvent onAccessibilityEvent()}
-each time an event occurs, receiving an {@link android.view.accessibility.AccessibilityEvent} object
+<p>To observe all the events during a test, create an implementation of {@link
+android.app.UiAutomation.OnAccessibilityEventListener} and pass it to {@link
+android.app.UiAutomation#setOnAccessibilityEventListener setOnAccessibilityEventListener()}.
+Your listener interface then receives a call to {@link
+android.app.UiAutomation.OnAccessibilityEventListener#onAccessibilityEvent onAccessibilityEvent()}
+each time an event occurs, receiving an {@link android.view.accessibility.AccessibilityEvent} object
that describes the event.</p>
-<p>There is a variety of other operations that the {@link android.app.UiAutomation} APIs expose
-at a very low level to encourage the development of UI test tools such as <a href="{@docRoot}tools/help/uiautomator/index.html">uiautomator</a>. For instance,
+<p>There is a variety of other operations that the {@link android.app.UiAutomation} APIs expose
+at a very low level to encourage the development of UI test tools such as <a href="{@docRoot}tools/help/uiautomator/index.html">uiautomator</a>. For instance,
{@link android.app.UiAutomation} can also:</p>
<ul>
<li>Inject input events
@@ -1039,16 +1034,16 @@
<li>Take screenshots
</ul>
-<p>And most importantly for UI test tools, the {@link android.app.UiAutomation} APIs work
+<p>And most importantly for UI test tools, the {@link android.app.UiAutomation} APIs work
across application boundaries, unlike those in {@link android.app.Instrumentation}.</p>
<h3 id="Systrace">Systrace events for apps</h3>
-<p>Android 4.3 adds the {@link android.os.Trace} class with two static methods,
-{@link android.os.Trace#beginSection beginSection()} and {@link android.os.Trace#endSection()},
-which allow you to define blocks of code to include with the systrace report. By creating
-sections of traceable code in your app, the systrace logs provide you a much more detailed
+<p>Android 4.3 adds the {@link android.os.Trace} class with two static methods,
+{@link android.os.Trace#beginSection beginSection()} and {@link android.os.Trace#endSection()},
+which allow you to define blocks of code to include with the systrace report. By creating
+sections of traceable code in your app, the systrace logs provide you a much more detailed
analysis of where slowdown occurs within your app.</p>
<p>For information about using the Systrace tool, read <a href="{@docRoot}tools/debugging/systrace.html">Analyzing Display and Performance with Systrace</a>.</p>
@@ -1058,31 +1053,31 @@
<h3 id="KeyStore">Android key store for app-private keys</h3>
-<p>Android now offers a custom Java Security Provider in the {@link java.security.KeyStore}
-facility, called Android Key Store, which allows you to generate and save private keys that
-may be seen and used by only your app. To load the Android Key Store, pass
-{@code "AndroidKeyStore"} to {@link java.security.KeyStore#getInstance(String)
+<p>Android now offers a custom Java Security Provider in the {@link java.security.KeyStore}
+facility, called Android Key Store, which allows you to generate and save private keys that
+may be seen and used by only your app. To load the Android Key Store, pass
+{@code "AndroidKeyStore"} to {@link java.security.KeyStore#getInstance(String)
KeyStore.getInstance()}.</p>
-<p>To manage your app's private credentials in the Android Key Store, generate a new key with
-{@link java.security.KeyPairGenerator} with {@link android.security.KeyPairGeneratorSpec}. First
-get an instance of {@link java.security.KeyPairGenerator} by calling {@link
-java.security.KeyPairGenerator#getInstance getInstance()}. Then call
-{@link java.security.KeyPairGenerator#initialize initialize()}, passing it an instance of
-{@link android.security.KeyPairGeneratorSpec}, which you can get using
-{@link android.security.KeyPairGeneratorSpec.Builder KeyPairGeneratorSpec.Builder}.
-Finally, get your {@link java.security.KeyPair} by calling {@link
+<p>To manage your app's private credentials in the Android Key Store, generate a new key with
+{@link java.security.KeyPairGenerator} with {@link android.security.KeyPairGeneratorSpec}. First
+get an instance of {@link java.security.KeyPairGenerator} by calling {@link
+java.security.KeyPairGenerator#getInstance getInstance()}. Then call
+{@link java.security.KeyPairGenerator#initialize initialize()}, passing it an instance of
+{@link android.security.KeyPairGeneratorSpec}, which you can get using
+{@link android.security.KeyPairGeneratorSpec.Builder KeyPairGeneratorSpec.Builder}.
+Finally, get your {@link java.security.KeyPair} by calling {@link
java.security.KeyPairGenerator#generateKeyPair generateKeyPair()}.</p>
<h3 id="HardwareKeyChain">Hardware credential storage</h3>
-<p>Android also now supports hardware-backed storage for your {@link android.security.KeyChain}
-credentials, providing more security by making the keys unavailable for extraction. That is, once
-keys are in a hardware-backed key store (Secure Element, TPM, or TrustZone), they can be used for
-cryptographic operations but the private key material cannot be exported. Even the OS kernel
-cannot access this key material. While not all Android-powered devices support storage on
-hardware, you can check at runtime if hardware-backed storage is available by calling
+<p>Android also now supports hardware-backed storage for your {@link android.security.KeyChain}
+credentials, providing more security by making the keys unavailable for extraction. That is, once
+keys are in a hardware-backed key store (Secure Element, TPM, or TrustZone), they can be used for
+cryptographic operations but the private key material cannot be exported. Even the OS kernel
+cannot access this key material. While not all Android-powered devices support storage on
+hardware, you can check at runtime if hardware-backed storage is available by calling
{@link android.security.KeyChain#isBoundKeyAlgorithm KeyChain.IsBoundKeyAlgorithm()}.</p>
@@ -1091,9 +1086,9 @@
<h3 id="ManifestFeatures">Declarable required features</h3>
-<p>The following values are now supported in the <a
+<p>The following values are now supported in the <a
href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a>
-element so you can ensure that your app is installed only on devices that provide the features
+element so you can ensure that your app is installed only on devices that provide the features
your app needs.</p>
<dl>
@@ -1137,8 +1132,8 @@
<h3 id="ManifestPermissions">User permissions</h3>
-<p>The following values are now supported in the <a
-href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code <uses-permission>}</a>
+<p>The following values are now supported in the <a
+href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code <uses-permission>}</a>
to declare the
permissions your app requires in order to access certain APIs.</p>
diff --git a/docs/html/distribute/googleplay/spotlight/index.jd b/docs/html/distribute/googleplay/spotlight/index.jd
index 88cdec4..7004b0a 100644
--- a/docs/html/distribute/googleplay/spotlight/index.jd
+++ b/docs/html/distribute/googleplay/spotlight/index.jd
@@ -22,6 +22,35 @@
margin-bottom:40px;
margin-top:30px;">
<div style="padding:0 0 0 29px;">
+ <h4>Developer Story: Colopl</h4>
+ <img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px height:78px;
+ width: 78px;
+ float: left;
+ margin: 17px 20px 9px 0;"
+ src="//lh3.ggpht.com/sx2ILNaXQYOsHfR91T5tUWGlfXE1FutHCBN02Fll6mi9gIaG6RZCGbeJMtIvOoegCPTh=w124" >
+ <div style="width:700px;">
+ <p style="margin-top:26px;
+ margin-bottom:12px;">
+ The creators of Kuma The Bear, Japan-based <a href="https://play.google.com/store/apps/developer?id=COLOPL,+Inc." target="_android">Colopl</a>, talk about how Google Play and Android allowed them to grow their business to become one of the most profitable games publishers in APAC to date. </p>
+ </div>
+ <iframe style="float:left;
+ margin-right:24px;
+ margin-top:14px;" width="700" height="394" src=
+ "http://www.youtube.com/embed/CbpoZeQCNe4?HD=1;rel=0;origin=developer.android.com;" frameborder="0" allowfullscreen>
+ </iframe>
+ </div>
+</div>
+
+<div style="background: #F0F0F0;
+ border-top: 1px solid #DDD;
+ padding: 0px 0 24px 0;
+ overflow: auto;
+ clear:both;
+ margin-bottom:40px;
+ margin-top:30px;">
+ <div style="padding:0 0 0 29px;">
<h4>Developer Story: redBus.in</h4>
<img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
-moz-border-radius: 5px;
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png
index 697fb7d..cc5b1af 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png
index 735262f..2625edb 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png
index cfb7952..9d91475 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png
index 5bb815a..f54a8af 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png
index 1be3b21..230bad4 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png
index 7e8aff2..2401d20 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png b/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png
index b5db82e..57b4c03 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_back.png
new file mode 100644
index 0000000..697fb7d
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_fore.png
new file mode 100644
index 0000000..735262f
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_shadow.png
new file mode 100644
index 0000000..cfb7952
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_back.png
new file mode 100644
index 0000000..5bb815a
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_fore.png
new file mode 100644
index 0000000..1be3b21
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_shadow.png
new file mode 100644
index 0000000..7e8aff2
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/thumb.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/thumb.png
new file mode 100644
index 0000000..b5db82e
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/thumb.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art.jd b/docs/html/distribute/promote/device-art.jd
index 58e183c..09a3941 100644
--- a/docs/html/distribute/promote/device-art.jd
+++ b/docs/html/distribute/promote/device-art.jd
@@ -173,12 +173,13 @@
title: 'Nexus 7',
url: 'http://www.google.com/nexus/7/',
physicalSize: 7,
- physicalHeight: 7.81,
- density: '213dpi',
+ physicalHeight: 8,
+ actualResolution: [1200,1920],
+ density: 'XHDPI',
landRes: ['shadow', 'back', 'fore'],
- landOffset: [315,270],
+ landOffset: [326,245],
portRes: ['shadow', 'back', 'fore'],
- portOffset: [264,311],
+ portOffset: [244,326],
portSize: [800,1280]
},
{
@@ -210,6 +211,20 @@
archived: true
},
{
+ id: 'nexus_7_2012',
+ title: 'Nexus 7 (2012)',
+ url: 'http://www.google.com/nexus/7/',
+ physicalSize: 7,
+ physicalHeight: 7.81,
+ density: '213dpi',
+ landRes: ['shadow', 'back', 'fore'],
+ landOffset: [315,270],
+ portRes: ['shadow', 'back', 'fore'],
+ portOffset: [264,311],
+ portSize: [800,1280],
+ archived: true
+ },
+ {
id: 'galaxy_nexus',
title: 'Galaxy Nexus',
url: 'http://www.android.com/devices/detail/galaxy-nexus',
diff --git a/docs/html/guide/topics/manifest/uses-sdk-element.jd b/docs/html/guide/topics/manifest/uses-sdk-element.jd
index d5b5bdf..15092a0 100644
--- a/docs/html/guide/topics/manifest/uses-sdk-element.jd
+++ b/docs/html/guide/topics/manifest/uses-sdk-element.jd
@@ -227,6 +227,12 @@
<table>
<tr><th>Platform Version</th><th>API Level</th><th>VERSION_CODE</th><th>Notes</th></tr>
+ <tr><td><a href="{@docRoot}about/versions/android-4.3.html">Android 4.3</a></td>
+ <td><a href="{@docRoot}sdk/api_diff/18/changes.html" title="Diff Report">18</a></td>
+ <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td>
+ <td><a href="{@docRoot}about/versions/jelly-bean.html">Platform
+Highlights</a></td></tr>
+
<tr><td><a href="{@docRoot}about/versions/android-4.2.html">Android 4.2, 4.2.2</a></td>
<td><a href="{@docRoot}sdk/api_diff/17/changes.html" title="Diff Report">17</a></td>
<td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}</td>
diff --git a/docs/html/images/video-Colopl.png b/docs/html/images/video-Colopl.png
new file mode 100644
index 0000000..0ee88c6
--- /dev/null
+++ b/docs/html/images/video-Colopl.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index a972309..73fc302 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -57,15 +57,15 @@
<li class="item carousel-home">
<div class="content-left col-11" style="padding-top:65px;">
<script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
- <div style="box-shadow: 3px 10px 18px 1px #999;width:600px;height:338px">
+ <div style="box-shadow: 3px 10px 18px 1px #999;width:600px;height:336px">
<div id="ytapiplayer">
- You need Flash player 8+ and JavaScript enabled to view this video.
+ <a href="http://www.youtube.com/watch?v=CbpoZeQCNe4"><img width=600 src="{@docRoot}images/video-Colopl.png"></a><!--You need Flash player 8+ and JavaScript enabled to view this video. -->
</div>
<script type="text/javascript">
var params = { allowScriptAccess: "always" };
var atts = { id: "ytapiplayer" };
- swfobject.embedSWF("//www.youtube.com/v/O8i4HUw7JYA?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1",
- "ytapiplayer", "600", "338", "8", null, null, params, atts);
+ swfobject.embedSWF("//www.youtube.com/v/CbpoZeQCNe4?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1",
+ "ytapiplayer", "600", "336", "8", null, null, params, atts);
// Callback used to pause/resume carousel based on video state
function onytplayerStateChange(newState) {
@@ -97,6 +97,37 @@
<p>Bangalore-based developers redBus.in talk about how Android is helping them deliver a superior booking and travel experience to millions of daily bus riders in India.</p>
</div>
</li>
+
+ <li class="item carousel-home">
+ <div class="content-left col-11" style="padding-top:10px;">
+ <a href="{@docRoot}channels/io2013.html">
+ <img src="{@docRoot}images/home/io-videos-2013.png" style="margin:60px 0 0;
+ box-shadow: 3px 10px 18px 1px #999;">
+ </a>
+ </div>
+ <div class="content-right col-4">
+ <h1>Watch the Android talks from Google I/O</h1>
+ <p>If you weren't able to attend Google I/O in person or couldn't make it
+ to all the talks, you can catch up on the action
+ with all the recordings, brought to you by
+ <a href="http://developers.google.com/live">Google Developers Live</a>.</p>
+ <p><a href="{@docRoot}channels/io2013.html" class="button"
+ >See the Android talks</a></p>
+ </div>
+ </li>
+
+
+ <li class="item carousel-home">
+ <div class="content-left col-9">
+ <a href="{@docRoot}about/versions/jelly-bean.html"><img src="{@docRoot}images/home/android-jellybean.png" ></a>
+ </div>
+ <div class="content-right col-6">
+ <h1>Android 4.2 Jelly Bean!</h1>
+ <p>The latest version of Jelly Bean is here, with performance optimizations, a refreshed UI, and great new features for developers. </p>
+ <p>Android 4.2 includes APIs for developing lock sceen widgets and Daydream screensavers, using external displays, creating RTL layouts, building flexible UI with nested Fragments, and much more.</p>
+ <p><a href="{@docRoot}about/versions/jelly-bean.html" class="button">Learn More</a></p>
+ </div>
+ </li>
<li class="item carousel-home">
<div class="content-left col-10">
<img src="{@docRoot}images/home/design.png" style="margin-top:30px">
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 0d0f348..aa3b2ec 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -5,43 +5,43 @@
page.metaDescription=Download the official Android SDK to develop apps for Android-powered devices.
-sdk.linux32_bundle_download=adt-bundle-linux-x86-20130522.zip
-sdk.linux32_bundle_bytes=439988972
-sdk.linux32_bundle_checksum=1fdd8d7537ab9217d61d32ab912f0243
+sdk.linux32_bundle_download=adt-bundle-linux-x86-20130717.zip
+sdk.linux32_bundle_bytes=440035305
+sdk.linux32_bundle_checksum=ecfacb91df1ee63cce1edd4f1a5cda5a
-sdk.linux64_bundle_download=adt-bundle-linux-x86_64-20130522.zip
-sdk.linux64_bundle_bytes=440275051
-sdk.linux64_bundle_checksum=e38751ff372a8d6208fcef5659133e98
+sdk.linux64_bundle_download=adt-bundle-linux-x86_64-20130717.zip
+sdk.linux64_bundle_bytes=440322117
+sdk.linux64_bundle_checksum=ab177a06784340b8f1d136651e3dc62a
-sdk.mac64_bundle_download=adt-bundle-mac-x86_64-20130522.zip
-sdk.mac64_bundle_bytes=409047751
-sdk.mac64_bundle_checksum=3f4d05206d66e402e87b27a6b3dcf0f9
+sdk.mac64_bundle_download=adt-bundle-mac-x86_64-20130717.zip
+sdk.mac64_bundle_bytes=411609229
+sdk.mac64_bundle_checksum=07c891212a49b5f8495ea9d8d47ba3fe
-sdk.win32_bundle_download=adt-bundle-windows-x86-20130522.zip
-sdk.win32_bundle_bytes=446736316
-sdk.win32_bundle_checksum=53345fa4121fa58cc048f66c3af3bae9
+sdk.win32_bundle_download=adt-bundle-windows-x86-20130717.zip
+sdk.win32_bundle_bytes=446783216
+sdk.win32_bundle_checksum=0dd91095999d3539ca1ec4033d83d935
-sdk.win64_bundle_download=adt-bundle-windows-x86_64-20130522.zip
-sdk.win64_bundle_bytes=446864400
-sdk.win64_bundle_checksum=b28817f62e7f54e3c683841b61b65564
+sdk.win64_bundle_download=adt-bundle-windows-x86_64-20130717.zip
+sdk.win64_bundle_bytes=446911629
+sdk.win64_bundle_checksum=61ec74995b39166db7f079017a028cec
-sdk.linux_download=android-sdk_r22.0.1-linux.tgz
-sdk.linux_bytes=105617062
-sdk.linux_checksum=56ed27d456b4f0e0d3090b24f9b06757
+sdk.linux_download=android-sdk_r22.0.4-linux.tgz
+sdk.linux_bytes=105640988
+sdk.linux_checksum=4a5db98a58c68c24e66f04f07ac77da5
-sdk.mac_download=android-sdk_r22.0.1-macosx.zip
-sdk.mac_bytes=77206237
-sdk.mac_checksum=5c20497d7f7b9d28ee30e57cbf769c8c
+sdk.mac_download=android-sdk_r22.0.4-macosx.zip
+sdk.mac_bytes=77225662
+sdk.mac_checksum=384752505f4f2ba3627bd6aad0697f11
-sdk.win_download=android-sdk_r22.0.1-windows.zip
-sdk.win_bytes=113483496
-sdk.win_checksum=cb7f7703450afa5914fb4b8b8332a9f3
+sdk.win_download=android-sdk_r22.0.4-windows.zip
+sdk.win_bytes=113507679
+sdk.win_checksum=320b11d1ed85fd3f5e937697c333d895
-sdk.win_installer=installer_r22.0.1-windows.exe
-sdk.win_installer_bytes=93479015
-sdk.win_installer_checksum=81621d3b164f81f91e066011b325f88f
+sdk.win_installer=installer_r22.0.4-windows.exe
+sdk.win_installer_bytes=93502726
+sdk.win_installer_checksum=96a8ae367d84ed219e1eb2cf473667d0
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index b8ab24b..2a09636 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -1,8 +1,8 @@
page.title=Installing the Eclipse Plugin
-adt.zip.version=22.0.1
-adt.zip.download=ADT-22.0.1.zip
-adt.zip.bytes=16815544
-adt.zip.checksum=64473af058fa8f02e36241ee378b3ac0
+adt.zip.version=22.0.4
+adt.zip.download=ADT-22.0.4.zip
+adt.zip.bytes=16838756
+adt.zip.checksum=f0291f4bb9d78ec34a7751cd2402cc2a
@jd:body
diff --git a/docs/html/tools/revisions/platforms.jd b/docs/html/tools/revisions/platforms.jd
index 31cec0e..820edbd 100644
--- a/docs/html/tools/revisions/platforms.jd
+++ b/docs/html/tools/revisions/platforms.jd
@@ -29,25 +29,46 @@
version.</p>
<p>To determine what revision of an Android platform you have installed, refer to the
-<strong>Installed Packages</strong> listing in the Android SDK Manager.</p>
+<strong>Installed Packages</strong> listing in the Android
+<a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a>.</p>
+
+<p class="caution"><strong>Important:</strong> To download the most recent Android
+system components from the Android SDK Manager, you must first update the SDK Tools to
+revision 22 or later and restart the SDK Manager. If you do not,
+the latest Android system components will not be available for download.</p>
+<h2 id="4.3">Android 4.3</h2>
-
-<h2 id="4.2">Android 4.2</h2>
-
-
-<p class="caution"><strong>Important:</strong> To download the new Android
-4.2.x system components from the Android SDK Manager, you must first update the
-SDK tools to revision 20 or later and restart the Android SDK Manager. If you do not,
-the Android 4.2 system components will not be available for download.</p>
-
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png"
+class="toggle-content-img" alt="" />Revision 1</a> <em>(July 2013)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+
+ <p>Initial release. The system version is 4.3.</p>
+ <dl>
+ <dt>Dependencies:</dt>
+ <dd>Android SDK Platform-tools r18 or higher is required.</dd>
+ <dd>Android SDK Tools 22.0.4 or higher is recommended.</dd>
+ </dl>
+
+ </div>
+</div>
+
+
+<h2 id="4.2">Android 4.2</h2>
+
+
+<div class="toggle-content closed">
+
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png"
class="toggle-content-img" alt="" />Revision 2</a> <em>(February 2013)</em>
</p>
@@ -150,12 +171,6 @@
<h2 id="4.1">Android 4.1</h2>
-<p class="caution"><strong>Important:</strong> To download the new Android
-4.1.x system components from the Android SDK Manager, you must first update the
-SDK tools to revision 20 or later and restart the Android SDK Manager. If you do not,
-the Android 4.1 system components will not be available for download.</p>
-
-
<div class="toggle-content closed">
<p><a href="#" onclick="return toggleContent(this)">
@@ -269,11 +284,6 @@
<h2 id="4.0.3">Android 4.0.3</h2>
-<p class="caution"><strong>Important:</strong> To download the new Android
-4.0.x system components from the Android SDK Manager, you must first update the
-SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not,
-the Android 4.0.x system components will not be available for download.</p>
-
<div class="toggle-content closed">
<p><a href="#" onclick="return toggleContent(this)">
@@ -711,13 +721,6 @@
<h2 id="2.3.4">Android 2.3.4</h2>
-
-<p>The sections below provide notes about successive releases of
-the Android 2.3.4 platform component for the Android SDK, as denoted by
-revision number. To determine what revision(s) of the Android
-2.3.4 platforms are installed in your SDK environment, refer to
-the "Installed Packages" listing in the Android SDK and AVD Manager.</p>
-
<div class="toggle-content closed" >
<p><a href="#" onclick="return toggleContent(this)">
@@ -865,14 +868,6 @@
<h2 id="2.3">Android 2.3</h2>
-
-<p>The sections below provide notes about successive releases of
-the Android 2.3 platform component for the Android SDK, as denoted by
-revision number. To determine what revision(s) of the Android
-2.3 platforms are installed in your SDK environment, refer to
-the "Installed Packages" listing in the Android SDK and AVD Manager.</p>
-
-
<div class="toggle-content closed" >
<p><a href="#" onclick="return toggleContent(this)">
@@ -1033,4 +1028,4 @@
<li>
WVGA854 (480x854 high density, normal screen)
</li>
-</ul>
\ No newline at end of file
+</ul>
diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd
index 52647ff..7b0b5a8 100644
--- a/docs/html/tools/sdk/eclipse-adt.jd
+++ b/docs/html/tools/sdk/eclipse-adt.jd
@@ -53,9 +53,46 @@
<p>For a summary of all known issues in ADT, see <a
href="http://tools.android.com/knownissues">http://tools.android.com/knownissues</a>.</p>
+
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>ADT 22.0.4</a> <em>(July 2013)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+<dl>
+ <dt>Dependencies:</dt>
+
+ <dd>
+ <ul>
+ <li>Java 1.6 or higher is required for ADT 22.0.4.</li>
+ <li>Eclipse Helios (Version 3.6.2) or higher is required for ADT 22.0.4.</li>
+ <li>ADT 22.0.4 is designed for use with <a href="{@docRoot}tools/sdk/tools-notes.html">SDK
+ Tools r22.0.4</a>. If you haven't already installed SDK Tools r22.0.4 into your SDK, use the
+ Android SDK Manager to do so.</li>
+ </ul>
+ </dd>
+
+ <dt>General Notes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed problem with compiling Renderscript code.</li>
+ <li>Improved Gradle export with better workflow and error reporting.</li>
+ <li>Improved Gradle multi-module export feature.</li>
+ <li>Updated build logic to force exporting of the classpath containers unless you are using
+ the Maven plugin.</li>
+ </ul>
+ </dd>
+
+</dl>
+</div>
+</div>
+
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>ADT 22.0.1</a> <em>(May 2013)</em>
</p>
diff --git a/docs/html/tools/sdk/ndk/index.jd b/docs/html/tools/sdk/ndk/index.jd
index 74caaf4..1f34987 100644
--- a/docs/html/tools/sdk/ndk/index.jd
+++ b/docs/html/tools/sdk/ndk/index.jd
@@ -1,29 +1,29 @@
ndk=true
page.template=sdk
-ndk.mac64_download=android-ndk-r8e-darwin-x86_64.tar.bz2
-ndk.mac64_bytes=508419298
-ndk.mac64_checksum=efac96fab20e6ddb1311d6ba5648ce72
+ndk.mac64_download=android-ndk-r9-darwin-x86_64.tar.bz2
+ndk.mac64_bytes=726430529
+ndk.mac64_checksum=b975271d8f064611e7e12bf87b736826
-ndk.mac32_download=android-ndk-r8e-darwin-x86.tar.bz2
-ndk.mac32_bytes=496238878
-ndk.mac32_checksum=e17e707464c45c0d5615e4d0ae6a5cf7
+ndk.mac32_download=android-ndk-r9-darwin-x86.tar.bz2
+ndk.mac32_bytes=710781553
+ndk.mac32_checksum=6f7c4dd38df9079bb4b13846add5c0da
-ndk.linux64_download=android-ndk-r8e-linux-x86_64.tar.bz2
-ndk.linux64_bytes=466853553
-ndk.linux64_checksum=fa812352956067e7a9eefc0274675e9a
+ndk.linux64_download=android-ndk-r9-linux-x86_64.tar.bz2
+ndk.linux64_bytes=669064468
+ndk.linux64_checksum=3eedc86b20ec09fcd1fd03f4481a706d
-ndk.linux32_download=android-ndk-r8e-linux-x86.tar.bz2
-ndk.linux32_bytes=461526099
-ndk.linux32_checksum=26d774b0884bcd98de08eb4de41ab532
+ndk.linux32_download=android-ndk-r9-linux-x86.tar.bz2
+ndk.linux32_bytes=660787157
+ndk.linux32_checksum=999d155ba772c49baacee6d41d664922
-ndk.win64_download=android-ndk-r8e-windows-x86_64.zip
-ndk.win64_bytes=461298980
-ndk.win64_checksum=11eb99b3b56fc86d9d231ebff5c41db3
+ndk.win64_download=android-ndk-r9-windows-x86_64.zip
+ndk.win64_bytes=826661995
+ndk.win64_checksum=cd56cc1036235f16369f2112fa27be91
-ndk.win32_download=android-ndk-r8e-windows-x86.zip
-ndk.win32_bytes=434701805
-ndk.win32_checksum=fb41ed2bff5610b14a7b6f085ab86213
+ndk.win32_download=android-ndk-r9-windows-x86.zip
+ndk.win32_bytes=777938252
+ndk.win32_checksum=9c1f66ff963cc61e338964c5f97a4d34
page.title=Android NDK
@jd:body
@@ -255,13 +255,286 @@
<h2 id="Revisions">Revisions</h2>
-<p>The sections below provide information and notes about successive releases of
-the NDK, as denoted by revision number. </p>
-
+<p>The following sections provide information about releases of the NDK.</p>
<div class="toggle-content opened">
+ <p>
+ <a href="#" onclick="return toggleContent(this)"> <img
+ src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+ >Android NDK, Revision 9</a> <em>(July 2013)</em>
+ </p>
+ <div class="toggle-content-toggleme">
+ <dl>
+ <dt>Important changes:</dt>
+ <dd>
+ <ul>
+ <li>Added support for Android 4.3 (API level 18). For more information, see
+ {@code STABLE-APIS.html} and new code examples in {@code samples/gles3jni/README}.
+ <li>Added headers and libraries for OpenGL ES 3.0, which is supported by Android 4.3
+ (API level 18) and higher.</li>
+ <li>Added GNU Compiler Collection (GCC) 4.8 compiler to the NDK. Since GCC 4.6 is still
+ the default, you must explicitly enable this option:
+ <ul>
+ <li>For {@code ndk-build} builds, export {@code NDK_TOOLCHAIN_VERSION=4.8} or
+ add it in {@code Application.mk}.</li>
+ <li>For standalone builds, use the {@code --toolchain=} option in
+ {@code make-standalone-toolchain.sh}, for example:<br>
+ {@code --toolchain=arm-linux-androideabi-4.8}</li>
+ </ul>
+ <p class="note"><strong>Note:</strong>
+ The {@code -Wunused-local-typedefs} option is enabled by {@code -Wall}. Be
+ sure to add {@code __attribute__((unused))} if you use compile-time asserts like
+ {@code sources/cxx-stl/stlport/stlport/stl/config/features.h}, line #311. For more
+ information, see
+ <a href="https://android-review.googlesource.com/#/c/55460">Change 55460</a></p>
+ <p class="note"><strong>Note:</strong>
+ In the GCC 4.7 release and later, ARM compilers generate unaligned access code by
+ default for ARMv6 and higher build targets. You may need to add the
+ {@code -mno-unaligned-access} build option when building for kernels that do not support
+ this feature.</p>
+ </li>
+ <li>Added Clang 3.3 support. The {@code NDK_TOOLCHAIN_VERSION=clang} build option
+ now picks Clang 3.3 by default.
+ <p class="note"><strong>Note:</strong>
+ Both GCC 4.4.3 and Clang 3.1 are deprecated, and will be removed from the next NDK
+ release.</p></li>
+ <li>Updated GNU Project Debugger (GDB) to support python 2.7.5.</li>
+ <li>Added MCLinker to support Windows hosts. Since {@code ld.gold}
+ is the default where available, you must add {@code -fuse-ld=mcld} in
+ {@code LOCAL_LDFLAGS} or {@code APP_LDFLAGS} to enable MCLinker.</li>
+ <li>Added {@code ndk-depends} tool which prints ELF library dependencies.
+ For more information, see {@code NDK-DEPENDS.html}.
+ (<a href="http://b.android.com/53486">Issue 53486</a>)</li>
+ </ul>
+ </dd>
+
+ <dt>Important bug fixes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed potential event handling issue in {@code android_native_app_glue}.
+ (<a href="http://b.android.com/41755">Issue 41755</a>)</li>
+ <li>Fixed ARM/GCC-4.7 build to generate sufficient alignment for NEON load and store
+ instructions VST and VLD.
+ (<a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57271">GCC Issue 57271</a>)</li>
+ <li>Fixed a GCC 4.4.3/4.6/4.7 internal compiler error (ICE) for a constant negative index
+ value on a string literal.
+ (<a href="http://b.android.com/54623">Issue 54623</a>)</li>
+ <li>Fixed GCC 4.7 segmentation fault for constant initialization with an object address.
+ (<a href="http://b.android.com/56508">Issue 56508</a>)</li>
+ <li>Fixed GCC 4.6 ARM segmentation fault for <code>-O</code> values when using Boost
+ 1.52.0. (<a href="http://b.android.com/42891">Issue 42891</a>)
+ <li>Fixed {@code libc.so} and {@code libc.a} to support the {@code wait4()} function.
+ (<a href="http://b.android.com/19854">Issue 19854</a>)</li>
+ <li>Updated the x86 libc.so and libc.a files to include the {@code clone()}
+ function.</li>
+ <li>Fixed {@code LOCAL_SHORT_COMMANDS} bug where the {@code linker.list} file is
+ empty or not used.</li>
+ <li>Fixed GCC MIPS build on Mac OS to use CFI directives, without which
+ {@code ld.mcld --eh-frame-hdr} fails frequently.</li>
+ <li>Fixed Clang 3.2 X86/MIPS internal compiler error in {@code llvm/lib/VMCore/Value.cpp}.
+ (<a href="https://android-review.googlesource.com/#/c/59021">Change 59021</a>)</li>
+ <li>Fixed GCC 4.7 64-bit Windows assembler crash. (Error: {@code out of memory allocating
+ 4294967280 bytes}).</li>
+ <li>Updated {@code ndk-gdb} script so that the {@code --start} or {@code --launch} actions
+ now wait for the GNU Debug Server, so that it can more reliably hit breakpoints set
+ early in the execution path (such as breakpoints in JNI code).
+ (<a href="http://b.android.com/41278">Issue 41278</a>)
+ <p class="note"><strong>Note:</strong>
+ This feature requires jdb and produces warning about pending breakpoints.
+ Specify the {@code --nowait} option to restore previous behavior.
+ </p>
+ </li>
+ <li>Fixed GDB crash when library list is empty.</li>
+ <li>Fixed GDB crash when using a {@code stepi} command past a {@code bx pc} or
+ {@code blx pc} Thumb instruction.
+ (<a href="http://b.android.com/56962">Issue 56962</a>,
+ <a href="http://b.android.com/36149">Issue 36149</a>)</li>
+ <li>Fixed MIPS {@code gdbserver} to look for {@code DT_MIPS_RLD_MAP} instead of
+ {@code DT_DEBUG}. (<a href="http://b.android.com/56586">Issue 56586</a>)</li>
+ <li>Fixed a circular dependency in the ndk-build script, for example: If A->B and
+ B->B, then B was dropped from build.
+ (<a href="http://b.android.com/56690">Issue 56690</a>)</li>
+ </ul>
+ </dd>
+
+ <dt>Other bug fixes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed the {@code ndk-build} script to enable you to specify a version of Clang as a
+ command line option (e.g., {@code NDK_TOOLCHAIN_VERSION=clang3.2}). Previously, only
+ specifying the version as an environment variable worked.</li>
+ <li>Fixed gabi++ size of {@code _Unwind_Exception} to be 24 for MIPS build targets when
+ using the Clang compiler.
+ (<a href="https://android-review.googlesource.com/#/c/54141">Change 54141</a>)</li>
+ <li>Fixed the {@code ndk-build} script to ensure that built libraries are actually
+ removed from projects that include prebuilt static libraries when using the
+ {@code ndk-build clean} command.
+ (<a href="https://android-review.googlesource.com/#/c/54461">Change 54461</a>,
+ <a href="https://android-review.googlesource.com/#/c/54480">Change 54480</a>)</li>
+ <li>Modified the {@code NDK_ANALYZE=1} option to be less verbose.</li>
+ <li>Fixed {@code gnu-libstdc++/Android.mk} to include a {@code backward/} path for builds
+ that use backward compability.
+ (<a href="http://b.android.com/53404">Issue 53404</a>)</li>
+ <li>Fixed a problem where {@code stlport new} sometimes returned random values.</li>
+ <li>Fixed {@code ndk-gdb} to match the order of {@code CPU_ABIS}, not {@code APP_ABIS}.
+ (<a href="http://b.android.com/54033">Issue 54033</a>)</li>
+ <li>Fixed a problem where the NDK 64-bit build on MacOSX choses the wrong path for
+ compiler.
+ (<a href="http://b.android.com/53769">Issue 53769</a>)</li>
+ <li>Fixed build scripts to detect 64-bit Windows Vista.
+ (<a href="http://b.android.com/54485">Issue 54485</a>)</li>
+ <li>Fixed x86 {@code ntonl/swap32} error: {@code invalid 'asm': operand number
+ out of range}.
+ (<a href="http://b.android.com/54465">Issue 54465</a>,
+ <a href="https://android-review.googlesource.com/#/c/57242">Change 57242</a>)</li>
+ <li>Fixed {@code ld.gold} to merge string literals.</li>
+ <li>Fixed {@code ld.gold} to handle large symbol alignment.</li>
+ <li>Updated {@code ld.gold} to enable the {@code --sort-section=name} option.</li>
+ <li>Fixed GCC 4.4.3/4.6/4.7 to suppress the {@code -export-dynamic} option for
+ statically linked programs. GCC no longer adds an {@code .interp} section for statically
+ linked programs.</li>
+ <li>Fixed GCC 4.4.3 {@code stlport} compilation error about inconsistent {@code typedef}
+ of {@code _Unwind_Control_Block}.
+ (<a href="http://b.android.com/54426">Issue 54426</a>)</li>
+ <li>Fixed {@code awk} scripts to handle {@code AndroidManifest.xml} files created on
+ Windows which may contain trailing {@code \r} characters and cause build errors.
+ (<a href="http://b.android.com/42548">Issue 42548</a>)</li>
+ <li>Fixed {@code make-standalone-toolchain.sh} to probe the {@code prebuilts/}
+ directory to detect if the host is 32 bit or 64 bit.</li>
+ <li>Fixed the Clang 3.2 {@code -integrated-as} option.</li>
+ <li>Fixed the Clang 3.2 ARM EHABI compact model {@code pr1} and {@code pr2} handler data.
+ </li>
+ <li>Added clang {@code -mllvm -arm-enable-ehabi} option to fix the following clang error:
+ <pre>clang: for the -arm-enable-ehabi option: may only occur zero or one times!</pre>
+ </li>
+ <li>Fixed build failure when there is no {@code uses-sdk} element in application
+ manifest. (<a href="http://b.android.com/57015">Issue 57015</a>)</li>
+ </ul>
+
+ </dd>
+ <dt>Other changes:</dt>
+ <dd>
+ <ul>
+ <li>Header Fixes
+ <ul>
+ <li>Modified headers to make {@code __set_errno} an inlined function, since
+ {@code __set_errno} in {@code errno.h} is deprecated, and {@code libc.so} no longer
+ exports it.</li>
+ <li>Modified {@code elf.h} to include {@code stdint.h}.
+ (<a href="http://b.android.com/55443">Issue 55443</a>)</li>
+ <li>Fixed {@code sys/un.h} to be included independently of other headers.
+ (<a href="http://b.android.com/53646">Issue 53646</a>)</li>
+ <li>Fixed all of the {@code MotionEvent_getHistorical} API family to take the
+ {@code const AInputEvent* motion_event}.
+ (<a href="http://b.android.com/55873">Issue 55873</a>)</li>
+ <li>Fixed {@code malloc_usable_size} to take {@code const void*}.
+ (<a href="http://b.android.com/55725">Issue 55725</a>)</li>
+ <li>Fixed stdint.h to be more compatible with C99.
+ (<a href="https://android-review.googlesource.com/#/c/46821">Change 46821</a>)</li>
+ <li>Modified {@code wchar.h} to not redefine {@code WCHAR_MAX} and
+ {@code WCHAR_MIN}</li>
+ <li>Fixed {@code <inttypes.h>} declaration for pointer-related {@code PRI} and
+ {@code SCN} macros. (<a href="http://b.android.com/57218">Issue 57218</a>)</li>
+ <li>Changed the {@code sys/cdefs.h} header so that {@code __WCHAR_TYPE__} is 32-bit
+ for API levels less than 9, which means that {@code wchat_t} is 32-bit for all
+ API levels. To restore the previous behavior, define the {@code _WCHAR_IS_8BIT}
+ boolean variable. (<a href="http://b.android.com/57267">Issue 57267</a>)</li>
+ </ul>
+ </li>
+ <li>Added more formatting in NDK {@code docs/} and miscellaneous documentation fixes.
+ </li>
+ <li>Added support for a thin archive technique when building static libraries.
+ (<a href="http://b.android.com/40303">Issue 40303</a>)</li>
+ <li>Updated script {@code make-standalone-toolchain.sh} to support the {@code stlport}
+ library in addition to {@code gnustl}, when you specify the option
+ {@code --stl=stlport}. For more information, see {@code STANDALONE-TOOLCHAIN.html}.</li>
+ <li>Updated the {@code make-standalone-toolchain.sh} script so that the
+ {@code --llvm-version=} option creates the {@code $TOOLCHAIN_PREFIX-clang} and
+ {@code $TOOLCHAIN_PREFIX-clang++} scripts in addition to {@code clang} and
+ {@code clang++}, to avoid using the host's clang and clang++ definitions by accident.
+ </li>
+ <li>Added two flags to re-enable two optimizations in upstream Clang but disabled in
+ NDK for better compatibility with code compiled by GCC:
+ <ul>
+ <li>Added a {@code -fcxx-missing-return-semantics} flag to re-enable <em>missing return
+ semantics</em> in Clang 3.2+. Normally, all paths should terminate with a return
+ statement for a value-returning function. If this is not the case, clang inserts
+ an undefined instruction (or trap in debug mode) at the path without a return
+ statement. If you are sure your code is correct, use this flag to allow the
+ optimizer to take advantage of the undefined behavior. If you are not sure, do not
+ use this flag. The caller may still receive a random incorrect value, but the
+ optimizer will not exploit it and make your code harder to debug.</li>
+ <li>Added a {@code -fglobal-ctor-const-promotion} flag to re-enable
+ promoting global variables with static constructor to be constants. With this flag,
+ the global variable optimization pass of LLVM tries to evaluate the global
+ variables with static constructors and promote them to global constants. Although
+ this optimization is correct, it may cause some incompatability with code compiled
+ by GCC. For example, code may do {@code const_cast} to cast the constant to mutable
+ and modify it. In GCC, the variable is in read-write and the code is run by
+ accident. In Clang, the const variable is in read-only memory and may cause your
+ application to crash.</li>
+ </ul>
+ </li>
+ <li>Added {@code -mldc1-sdc1} to the MIPS GCC and Clang compilers. By default, compilers
+ align 8-byte objects properly and emit the {@code ldc1} and {@code sdc1} instructions
+ to move them around. If your app uses a custom allocator that does not always align
+ with a new object's 8-byte boundary in the same way as the default allocator, your app
+ may crash due to {@code ldc1} and {@code sdc1} operations on unaligned memory. In this
+ case, use the {@code -mno-ldc1-sdc1} flag to workaround the problem.</li>
+ <li>Downgraded the event severity from warning to info if {@code APP_PLATFORM_LEVEL} is
+ larger than {@code APP_MIN_PLATFORM_LEVEL}. The {@code APP_PLATFORM_LEVEL} may be lower
+ than {@code APP_PLATFORM} in {@code jni/Application.mk} because the NDK does not have
+ headers for all levels. In this case, the actual level is shifted downwards. The
+ {@code APP_MIN_PLATFORM_LEVEL} is specified by the {@code android:minSdkVersion} in
+ your application's manifest.
+ (<a href="http://b.android.com/39752">Issue 39752</a>)</li>
+ <li>Added the {@code android_getCpuIdArm()} and {@code android_setCpuArm()} methods to
+ {@code cpu-features.c}. This addition enables easier retrieval of the ARM CPUID
+ information. (<a href="http://b.android.com/53689">Issue 53689</a>)</li>
+ <li>Modified {@code ndk-build} to use GCC 4.7's {@code as/ld} for Clang compiling.
+ <p class="note"><strong>Note:</strong>
+ In GCC 4.7, {@code monotonic_clock} and {@code is_monotonic} have been renamed to
+ {@code steady_clock} and {@code is_steady}, respectively.</p></li>
+ <li>Added the following new warnings to the {@code ndk-build} script:
+ <ul>
+ <li>Added warnings if {@code LOCAL_LDLIBS/LDFLAGS} are used in static library
+ modules.</li>
+ <li>Added a warning if a configuration has no module to build.</li>
+ <li>Added a warning for non-system libraries being used in
+ {@code LOCAL_LDLIBS/LDFLAGS} of a shared library or executable modules.</li>
+ </ul>
+ </li>
+ <li>Updated build scripts, so that if {@code APP_MODULES} is not defined and only static
+ libraries are listed in {@code Android.mk}, the script force-builds all of them.
+ (<a href="http://b.android.com/53502">Issue 53502</a>)</li>
+ <li>Updated {@code ndk-build} to support absolute paths in {@code LOCAL_SRC_FILES}.</li>
+ <li>Removed the {@code *-gdbtui} executables, which are duplicates of the {@code *-gdb}
+ executables with the {@code -tui} option enabled.</li>
+ <li>Updated the build scripts to warn you when the Edison Design Group (EDG) compiler
+ front-end turns {@code _STLP_HAS_INCLUDE_NEXT} back on.
+ (<a href="http://b.android.com/53646">Issue 53646</a>)</li>
+ <li>Added the environment variable {@code NDK_LIBS_OUT} to allow overriding of the
+ path for {@code libraries/gdbserver} from the default {@code $PROJECT/libs}.
+ For more information, see {@code OVERVIEW.html}.</li>
+ <li>Changed ndk-build script defaults to compile code with format string protection
+ {@code -Wformat -Werror=format-security}. You may set
+ {@code LOCAL_DISABLE_FORMAT_STRING_CHECKS=true} to disable it.
+ For more information, see {@code ANDROID-MK.html}</li>
+ <li>Added STL pretty-print support in {@code ndk-gdb-py}. For more information, see
+ {@code NDK-GDB.html}.</li>
+ <li>Added tests based on the googletest frameworks.</li>
+ <li>Added a notification to the toolchain build script that warns you if the current shell
+ is not {@code bash}.</li>
+ </ul>
+ </dd>
+ </dl>
+ </div>
+</div>
+
+
+<div class="toggle-content closed">
<p><a href="#" onclick="return toggleContent(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt="">Android NDK, Revision 8e</a> <em>(March 2013)</em>
</p>
@@ -283,7 +556,7 @@
build automatically sorts out the order of libraries specified in
{@code LOCAL_STATIC_LIBRARIES}, {@code LOCAL_WHOLE_STATIC_LIBRARIES} and
{@code LOCAL_SHARED_LIBRARIES}. For more information, see {@code CHANGES.HTML}.
- (<a href="http://code.google.com/p/android/issues/detail?id=39378">Issue 39378</a>)</li>
+ (<a href="http://b.android.com/39378">Issue 39378</a>)</li>
</ul>
</dd>
@@ -295,23 +568,23 @@
<li>Fixed build script which unconditionally builds Clang/llvm for MacOSX in 64-bit.</li>
<li>Fixed GCC 4.6/4.7 internal compiler error:
{@code gen_thumb_movhi_clobber at config/arm/arm.md:5832}.
- (<a href="http://code.google.com/p/android/issues/detail?id=52732">Issue 52732</a>)</li>
+ (<a href="http://b.android.com/52732">Issue 52732</a>)</li>
<li>Fixed build problem where GCC/ARM 4.6/4.7 fails to link code using 64-bit atomic
built-in functions.
- (<a href="http://code.google.com/p/android/issues/detail?id=41297">Issue 41297</a>)</li>
+ (<a href="http://b.android.com/41297">Issue 41297</a>)</li>
<li>Fixed GCC 4.7 linker DIV usage mismatch errors.
(<a href="http://sourceware.org/ml/binutils/2012-12/msg00202.html">Sourceware Issue</a>)
<li>Fixed GCC 4.7 internal compiler error {@code build_data_member_initialization, at
cp/semantics.c:5790}.</li>
<li>Fixed GCC 4.7 internal compiler error {@code redirect_eh_edge_1, at tree-eh.c:2214}.
- (<a href="http://code.google.com/p/android/issues/detail?id=52909">Issue 52909</a>)</li>
+ (<a href="http://b.android.com/52909">Issue 52909</a>)</li>
<li>Fixed a GCC 4.7 segfault.
(<a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55245">GCC Issue</a>)</li>
<li>Fixed {@code <chrono>} clock resolution and enabled {@code steady_clock}.
- (<a href="http://code.google.com/p/android/issues/detail?id=39680">Issue 39680</a>)</li>
+ (<a href="http://b.android.com/39680">Issue 39680</a>)</li>
<li>Fixed toolchain to enable {@code _GLIBCXX_HAS_GTHREADS} for GCC 4.7 libstdc++.
- (<a href="http://code.google.com/p/android/issues/detail?id=41770">Issue 41770</a>,
- <a href="http://code.google.com/p/android/issues/detail?id=41859">Issue 41859</a>)</li>
+ (<a href="http://b.android.com/41770">Issue 41770</a>,
+ <a href="http://b.android.com/41859">Issue 41859</a>)</li>
<li>Fixed problem with the X86 MXX/SSE code failing to link due to missing
{@code posix_memalign}.
(<a href="https://android-review.googlesource.com/#/c/51872">Change 51872</a>)</li>
@@ -321,24 +594,24 @@
<li>Fixed GCC4.7/X86 to restore earlier {@code cmov} behavior.
(<a href="http://gcc.gnu.org/viewcvs?view=revision&revision=193554">GCC Issue</a>)</li>
<li>Fixed handling NULL return value of {@code setlocale()} in libstdc++/GCC4.7.
- (<a href="http://code.google.com/p/android/issues/detail?id=46718">Issue 46718</a>)
+ (<a href="http://b.android.com/46718">Issue 46718</a>)
<li>Fixed {@code ld.gold} runtime undefined reference to {@code __exidx_start} and
{@code __exidx_start_end}.
(<a href="https://android-review.googlesource.com/#/c/52134">Change 52134</a>)</li>
<li>Fixed Clang 3.1 internal compiler error when using Eigen library.
- (<a href="http://code.google.com/p/android/issues/detail?id=41246">Issue 41246</a>)</li>
+ (<a href="http://b.android.com/41246">Issue 41246</a>)</li>
<li>Fixed Clang 3.1 internal compiler error including {@code <chrono>} in C++11 mode.
- (<a href="http://code.google.com/p/android/issues/detail?id=39600">Issue 39600</a>)</li>
+ (<a href="http://b.android.com/39600">Issue 39600</a>)</li>
<li>Fixed Clang 3.1 internal compiler error when generating object code for a method
call to a uniform initialized {@code rvalue}.
- (<a href="http://code.google.com/p/android/issues/detail?id=41387">Issue 41387</a>)</li>
+ (<a href="http://b.android.com/41387">Issue 41387</a>)</li>
<li>Fixed Clang 3.1/X86 stack realignment.
(<a href="https://android-review.googlesource.com/#/c/52154">Change 52154</a>)</li>
<li>Fixed problem with GNU Debugger (GDB) SIGILL when debugging on Android 4.1.2.
- (<a href="http://code.google.com/p/android/issues/detail?id=40941">Issue 40941</a>)</li>
+ (<a href="http://b.android.com/40941">Issue 40941</a>)</li>
<li>Fixed problem where GDB cannot set {@code source:line} breakpoints when symbols contain
long, indirect file paths.
- (<a href="http://code.google.com/p/android/issues/detail?id=42448">Issue 42448</a>)</li>
+ (<a href="http://b.android.com/42448">Issue 42448</a>)</li>
<li>Fixed GDB {@code read_program_header} for MIPS PIE executables.
(<a href="https://android-review.googlesource.com/#/c/49592">Change 49592</a>)</li>
<li>Fixed {@code STLport} segmentation fault in {@code uncaught_exception()}.
@@ -346,7 +619,7 @@
<li>Fixed {@code STLport} bus error in exception handling due to unaligned access of
{@code DW_EH_PE_udata2}, {@code DW_EH_PE_udata4}, and {@code DW_EH_PE_udata8}.</li>
<li>Fixed Gabi++ infinite recursion problem with {@code nothrow new[]} operator.
- (<a href="http://code.google.com/p/android/issues/detail?id=52833">Issue 52833</a>)</li>
+ (<a href="http://b.android.com/52833">Issue 52833</a>)</li>
<li>Fixed Gabi++ wrong offset to exception handler pointer.
(<a href="https://android-review.googlesource.com/#/c/53446">Change 53446</a>)</li>
<li>Removed Gabi++ redundant free on exception object
@@ -365,11 +638,11 @@
<li>Fixed {@code stddef.h} to not redefine {@code offsetof} since it already exists
in the toolchain.</li>
<li>Fixed {@code elf.h} to contain {@code Elf32_auxv_t} and {@code Elf64_auxv_t}.
- (<a href="http://code.google.com/p/android/issues/detail?id=38441">Issue 38441</a>)
+ (<a href="http://b.android.com/38441">Issue 38441</a>)
</li>
<li>Fixed the {@code #ifdef} C++ definitions in the
{@code OpenSLES_AndroidConfiguration.h} header file.
- (<a href="http://code.google.com/p/android/issues/detail?id=53163">Issue 53163</a>)
+ (<a href="http://b.android.com/53163">Issue 53163</a>)
</li>
</ul>
</li>
@@ -377,7 +650,7 @@
</li>
<li>Fixed system and Gabi++ headers to be able to compile with API level 8 and lower.</li>
<li>Fixed {@code cpufeatures} to not parse {@code /proc/self/auxv}.
- (<a href="http://code.google.com/p/android/issues/detail?id=43055">Issue 43055</a>)</li>
+ (<a href="http://b.android.com/43055">Issue 43055</a>)</li>
<li>Fixed {@code ld.gold} to not depend on host libstdc++ and on Windows platforms,
to not depend on the {@code libgcc_sjlj_1.dll} library.</li>
<li>Fixed Clang 3.1 which emits inconsistent register list in {@code .vsave} and fails
@@ -394,16 +667,16 @@
</li>
<li>Fixed X86 {@code libc.so} and {@code lib.a} which were missing the {@code sigsetjmp}
and {@code siglongjmp} functions already declared in {@code setjmp.h}.
- (<a href="http://code.google.com/p/android/issues/detail?id=19851">Issue 19851</a>)</li>
+ (<a href="http://b.android.com/19851">Issue 19851</a>)</li>
<li>Patched GCC 4.4.3/4.6/4.7 libstdc++ to work with Clang in C++ 11.
(<a href="http://clang.llvm.org/cxx_status.html">Clang Issue</a>)</li>
<li>Fixed cygwin path in argument passed to {@code HOST_AWK}.</li>
<li>Fixed {@code ndk-build} script warning in windows when running from project's JNI
directory.
- (<a href="http://code.google.com/p/android/issues/detail?id=40192">Issue 40192</a>)</li>
+ (<a href="http://b.android.com/40192">Issue 40192</a>)</li>
<li>Fixed problem where the {@code ndk-build} script does not build if makefile has
trailing whitespace in the {@code LOCAL_PATH} definition.
- (<a href="http://code.google.com/p/android/issues/detail?id=42841">Issue 42841</a>)</li>
+ (<a href="http://b.android.com/42841">Issue 42841</a>)</li>
</ul>
</dd>
@@ -419,13 +692,13 @@
hidden visibility except for exception handling helpers.</li>
<li>Updated build so that {@code STLport} is built for ARM in Thumb mode.</li>
<li>Added support for {@code std::set_new_handler} in Gabi++.
- (<a href="http://code.google.com/p/android/issues/detail?id=52805">Issue 52805</a>)</li>
+ (<a href="http://b.android.com/52805">Issue 52805</a>)</li>
<li>Enabled {@code FUTEX} system call in GNU libstdc++.</li>
<li>Updated {@code ndk-build} so that it no longer copies prebuilt static library to
a project's {@code obj/local/<abi>/} directory.
- (<a href="http://code.google.com/p/android/issues/detail?id=40302">Issue 40302</a>)</li>
+ (<a href="http://b.android.com/40302">Issue 40302</a>)</li>
<li>Removed {@code __ARM_ARCH_5*__} from ARM {@code toolchains/*/setup.mk} script.
- (<a href="http://code.google.com/p/android/issues/detail?id=21132">Issue 21132</a>)</li>
+ (<a href="http://b.android.com/21132">Issue 21132</a>)</li>
<li>Built additional GNU libstdc++ libraries in thumb for ARM.</li>
<li>Enabled MIPS floating-point {@code madd/msub/nmadd/nmsub/recip/rsqrt}
instructions with 32-bit FPU.</li>
@@ -458,7 +731,7 @@
which was preventing a significant amount of parallel build processing.</li>
<li>Updated {@code build-gabi++.sh} and {@code build-stlport.sh} so they can now run
from the NDK package.
- (<a href="http://code.google.com/p/android/issues/detail?id=52835">Issue 52835</a>)
+ (<a href="http://b.android.com/52835">Issue 52835</a>)
</li>
<li>Fixed {@code run-tests.sh} in the {@code MSys} utilities collection.</li>
<li>Improved 64-bit host toolchain and Canadian Cross build support.</li>
@@ -540,7 +813,7 @@
<dd>
<ul>
<li>Fixed unnecessary rebuild of object files when using the {@code ndk-build} script.
- (<a href="http://code.google.com/p/android/issues/detail?id=39810">Issue 39810</a>)</li>
+ (<a href="http://b.android.com/39810">Issue 39810</a>)</li>
<li>Fixed a linker failure with the NDK 8c release for Mac OS X 10.6.x that produced the
following error:
<pre>
@@ -551,29 +824,29 @@
not compatible with Mac OS 10.6.x and the NDK.
</li>
<li>Removed the {@code -x c++} options from the Clang++ standalone build script.
- (<a href="http://code.google.com/p/android/issues/detail?id=39089">Issue 39089</a>)</li>
+ (<a href="http://b.android.com/39089">Issue 39089</a>)</li>
<li>Fixed issues using the {@code NDK_TOOLCHAIN_VERSION=clang3.1} option in Cygwin.
- (<a href="http://code.google.com/p/android/issues/detail?id=39585">Issue 39585</a>)</li>
+ (<a href="http://b.android.com/39585">Issue 39585</a>)</li>
<li>Fixed the {@code make-standalone-toolchain.sh} script to allow generation of a
standalone toolchain using the Cygwin or MinGW environments. The resulting toolchain
can be used in Cygwin, MingGW or CMD.exe environments.
- (<a href="http://code.google.com/p/android/issues/detail?id=39915">Issue 39915</a>,
- <a href="http://code.google.com/p/android/issues/detail?id=39585">Issue 39585</a>)</li>
+ (<a href="http://b.android.com/39915">Issue 39915</a>,
+ <a href="http://b.android.com/39585">Issue 39585</a>)</li>
<li>Added missing {@code SL_IID_ANDROIDBUFFERQUEUESOURCE} option in android-14 builds for
ARM and X86.
- (<a href="http://code.google.com/p/android/issues/detail?id=40625">Issue 40625</a>)</li>
+ (<a href="http://b.android.com/40625">Issue 40625</a>)</li>
<li>Fixed x86 CPU detection for the {@code ANDROID_CPU_X86_FEATURE_MOVBE} feature.
- (<a href="http://code.google.com/p/android/issues/detail?id=39317">Issue 39317</a>)</li>
+ (<a href="http://b.android.com/39317">Issue 39317</a>)</li>
<li>Fixed an issue preventing the Standard Template Library (STL) from using C++
sources that do not have a {@code .cpp} file extension.</li>
<li>Fixed GCC 4.6 ARM internal compiler error <em>at reload1.c:1061</em>.
- (<a href="http://code.google.com/p/android/issues/detail?id=20862">Issue 20862</a>)</li>
+ (<a href="http://b.android.com/20862">Issue 20862</a>)</li>
<li>Fixed GCC 4.4.3 ARM internal compiler error <em>at emit-rtl.c:1954</em>.
- (<a href="http://code.google.com/p/android/issues/detail?id=22336">Issue 22336</a>)</li>
+ (<a href="http://b.android.com/22336">Issue 22336</a>)</li>
<li>Fixed GCC 4.4.3 ARM internal compiler error <em>at postreload.c:396</em>.
- (<a href="http://code.google.com/p/android/issues/detail?id=22345">Issue 22345</a>)</li>
+ (<a href="http://b.android.com/22345">Issue 22345</a>)</li>
<li>Fixed problem with GCC 4.6/4.7 skipping lambda functions.
- (<a href="http://code.google.com/p/android/issues/detail?id=35933">Issue 35933</a>)</li>
+ (<a href="http://b.android.com/35933">Issue 35933</a>)</li>
</ul>
</dd>
@@ -584,21 +857,21 @@
<ul>
<li>Fixed {@code __WINT_TYPE__} and {@code wint_t} to be the same type.</li>
<li>Corrected typo in {@code android/bitmap.h}.
- (<a href="http://code.google.com/p/android/issues/detail?id=15134">Issue 15134</a>)
+ (<a href="http://b.android.com/15134">Issue 15134</a>)
</li>
<li>Corrected typo in {@code errno.h}.</li>
<li>Added check for the presence of {@code __STDC_VERSION__} in {@code sys/cdefs.h}.
- (<a href="http://code.google.com/p/android/issues/detail?id=14627">Issue 14627</a>)
+ (<a href="http://b.android.com/14627">Issue 14627</a>)
</li>
<li>Reorganized headers in {@code byteswap.h} and {@code dirent.h}.</li>
<li>Fixed {@code limits.h} to include {@code page.h} which provides {@code PAGE_SIZE}
settings.
- (<a href="http://code.google.com/p/android/issues/detail?id=39983">Issue 39983</a>)
+ (<a href="http://b.android.com/39983">Issue 39983</a>)
</li>
<li>Fixed return type of {@code glGetAttribLocation()} and
{@code glGetUniformLocation()} from {@code int} to {@code GLint}.</li>
<li>Fixed {@code __BYTE_ORDER} constant for x86 builds.
- (<a href="http://code.google.com/p/android/issues/detail?id=39824">Issue 39824</a>)
+ (<a href="http://b.android.com/39824">Issue 39824</a>)
</li>
</ul>
</li>
@@ -611,7 +884,7 @@
<li>Fixed ARM EHABI support in Clang to conform to specifications.</li>
<li>Fixed GNU Debugger (GDB) to shorten the time spent on walking the target's link map
during {@code solib} events.
- (<a href="http://code.google.com/p/android/issues/detail?id=38402">Issue 38402</a>)</li>
+ (<a href="http://b.android.com/38402">Issue 38402</a>)</li>
<li>Fixed missing {@code libgcc.a} file when linking shared libraries.</li>
</ul>
</dd>
@@ -712,7 +985,7 @@
<ul>
<li>Fixed an issue where running {@code make-standalone-toolchain.sh} with root privileges
resulted in the stand alone tool chain being inaccessible to some users.
- (<a href="http://code.google.com/p/android/issues/detail?id=35279">Issue 35279</a>)
+ (<a href="http://b.android.com/35279">Issue 35279</a>)
<ul>
<li>All files and executables in the NDK release package are set to have read and
execute permissions for all.</li>
@@ -722,23 +995,23 @@
<li>Removed redundant {@code \r} from Windows prebuilt {@code echo.exe}. The redundant
{@code \r} caused {@code gdb.setup} to fail in the GNU Debugger (GDB) because it
incorrectly became part of the path.
- (<a href="http://code.google.com/p/android/issues/detail?id=36054">Issue 36054</a>)</li>
+ (<a href="http://b.android.com/36054">Issue 36054</a>)</li>
<li>Fixed Windows parallel builds that sometimes failed due to timing issues in the
{@code host-mkdir} implementation.
- (<a href="http://code.google.com/p/android/issues/detail?id=25875">Issue 25875</a>)</li>
+ (<a href="http://b.android.com/25875">Issue 25875</a>)</li>
<li>Fixed GCC 4.4.3 GNU {@code libstdc++} to <em>not</em> merge {@code typeinfo} names by
default. For more details, see
{@code toolchain repo gcc/gcc-4.4.3/libstdc++-v3/libsupc++/typeinfo}.
- (<a href="http://code.google.com/p/android/issues/detail?id=22165">Issue 22165</a>)</li>
+ (<a href="http://b.android.com/22165">Issue 22165</a>)</li>
<li>Fixed problem on {@code null} context in GCC 4.6
{@code cp/mangle.c::write_unscoped_name}, where GCC may crash when the context is
{@code null} and dereferenced in {@code TREE_CODE}.</li>
<li>Fixed GCC 4.4.3 crashes on ARM NEON-specific type definitions for floats.
- (<a href="http://code.google.com/p/android/issues/detail?id=34613">Issue 34613</a>)</li>
+ (<a href="http://b.android.com/34613">Issue 34613</a>)</li>
<li>Fixed the {@code STLport} internal {@code _IteWrapper::operator*()} implementation
where a stale stack location holding the dereferenced value was returned and caused
runtime crashes.
- (<a href="http://code.google.com/p/android/issues/detail?id=38630">Issue 38630</a>)</li>
+ (<a href="http://b.android.com/38630">Issue 38630</a>)</li>
<li>ARM-specific fixes:
<ul>
@@ -755,17 +1028,17 @@
<li>Fixed {@code binutils-2.21/ld.bfd} to be capable of linking object from older
binutils without {@code tag_FP_arch}, which was producing <em>assertion fail</em>
error messages in GNU Binutils.
- (<a href="http://code.google.com/p/android/issues/detail?id=35209">Issue 35209</a>)
+ (<a href="http://b.android.com/35209">Issue 35209</a>)
</li>
<li>Removed <em>Unknown EABI object attribute 44</em> warning when
{@code binutils-2.19/ld} links prebuilt object by newer {@code binutils-2.21}</li>
<li>Fixed an issue in GNU {@code stdc++} compilation with both {@code -mthumb} and
{@code -march=armv7-a}, by modifying {@code make-standalone-toolchain.sh} to populate
{@code headers/libs} in sub-directory {@code armv7-a/thumb}.
- (<a href="http://code.google.com/p/android/issues/detail?id=35616">Issue 35616</a>)
+ (<a href="http://b.android.com/35616">Issue 35616</a>)
</li>
<li>Fixed <em>unresolvable R_ARM_THM_CALL relocation</em> error.
- (<a href="http://code.google.com/p/android/issues/detail?id=35342">Issue 35342</a>)
+ (<a href="http://b.android.com/35342">Issue 35342</a>)
</li>
<li>Fixed internal compiler error at {@code reload1.c:3633}, caused by the ARM
back-end expecting the wrong operand type when sign-extend from {@code char}.
@@ -794,11 +1067,11 @@
<li>Disabled Python support in gdb-7.x at build, otherwise the gdb-7.x configure
function may pick up whatever Python version is available on the host and build
{@code gdb} with a hard-wired dependency on a specific version of Python.
- (<a href="http://code.google.com/p/android/issues/detail?id=36120">Issue 36120</a>)
+ (<a href="http://b.android.com/36120">Issue 36120</a>)
</li>
<li>Fixed {@code ndk-gdb} when {@code APP_ABI} contains {@code all} and matchs none
of the known architectures.
- (<a href="http://code.google.com/p/android/issues/detail?id=35392">Issue 35392</a>)
+ (<a href="http://b.android.com/35392">Issue 35392</a>)
</li>
<li>Fixed Windows pathname support, by keeping the {@code :} character if it looks
like it could be part of a Windows path starting with a drive letter.
@@ -809,7 +1082,7 @@
</li>
<li>Added fix to only read the current {@code solibs} when the linker is consistent.
This change speeds up {@code solib} event handling.
- (<a href="http://code.google.com/p/android/issues/detail?id=37677">Issue 37677</a>)
+ (<a href="http://b.android.com/37677">Issue 37677</a>)
</li>
<li>Added fix to make repeated attempts to find {@code solib} breakpoints. GDB now
retries {@code enable_break()} during every call to {@code svr4_current_sos()} until
@@ -817,13 +1090,13 @@
(<a href="https://android-review.googlesource.com/#/c/43563">Change 43563</a>)</li>
<li>Fixed an issue where {@code gdb} would not stop on breakpoints placed in
{@code dlopen-ed} libraries.
- (<a href="http://code.google.com/p/android/issues/detail?id=34856">Issue 34856</a>)
+ (<a href="http://b.android.com/34856">Issue 34856</a>)
</li>
<li>Fixed {@code SIGILL} in dynamic linker when calling {@code dlopen()}, on system
where {@code /system/bin/linker} is stripped of symbols and
{@code rtld_db_dlactivity()} is implemented as {@code Thumb}, due to not preserving
{@code LSB} of {@code sym_addr}.
- (<a href="http://code.google.com/p/android/issues/detail?id=37147">Issue 37147</a>)
+ (<a href="http://b.android.com/37147">Issue 37147</a>)
</li>
</ul>
</li>
@@ -848,7 +1121,7 @@
{@code __END_DECLS}.</li>
<li>Removed unimplemented functions in {@code malloc.h}.</li>
<li>Fixed {@code stdint.h} defintion of {@code uint64_t} for ANSI compilers.
- (<a href="http://code.google.com/p/android/issues/detail?id=1952">Issue 1952</a>)</li>
+ (<a href="http://b.android.com/1952">Issue 1952</a>)</li>
<li>Fixed preprocessor macros in {@code <arch>/include/machine/*}.</li>
<li>Replaced {@code link.h} for MIPS with new version supporting all platforms.</li>
<li>Removed {@code linux-unistd.h}</li>
@@ -904,7 +1177,7 @@
{@code platforms/android-[3,4,5,8]}. Those headers were incomplete, since both X86 and
MIPS ABIs are only supported at API 9 or higher.</li>
<li>Simplified c++ include path in standalone packages, as shown below.
- (<a href="http://code.google.com/p/android/issues/detail?id=35279">Issue 35279</a>)
+ (<a href="http://b.android.com/35279">Issue 35279</a>)
<pre>
<path>/arm-linux-androideabi/include/c++/4.6.x-google
to:
@@ -916,7 +1189,7 @@
<li>Fixed an issue in {@code samples/san-angeles} that caused a black screen or freeze
frame on re-launch.</li>
<li>Replaced deprecated APIs in NDK samples.
- (<a href="http://code.google.com/p/android/issues/detail?id=20017">Issue 20017</a>)
+ (<a href="http://b.android.com/20017">Issue 20017</a>)
<ul>
<li>{@code hello-gl2} from android-5 to android-7</li>
<li>{@code native-activity} from android-9 to android-10</li>
@@ -1196,7 +1469,7 @@
<li>Fixed a typo in GAbi++ implementation where the result of {@code
dynamic_cast<D>(b)} of base class object {@code b} to derived class {@code D} is
incorrectly adjusted in the opposite direction from the base class.
- (<a href="http://code.google.com/p/android/issues/detail?id=28721">Issue 28721</a>)
+ (<a href="http://b.android.com/28721">Issue 28721</a>)
</li>
<li>Fixed an issue in which {@code make-standalone-toolchain.sh} fails to copy
{@code libsupc++.*}.</li>
@@ -1710,7 +1983,7 @@
<li>Fixed the standalone toolchain linker warnings about missing the definition and
size for the <code>__dso_handle</code> symbol (ARM only).</li>
<li>Fixed the inclusion order of <code>$(SYSROOT)/usr/include</code> for x86 builds.
- See the <a href="http://code.google.com/p/android/issues/detail?id=18540">bug</a> for
+ See the <a href="http://b.android.com/18540">bug</a> for
more information.</li>
<li>Fixed the definitions of <code>ptrdiff_t</code> and <code>size_t</code> in
x86-specific systems when they are used with the x86 standalone toolchain.</li>
diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd
index b58fdd1..cd2d986 100644
--- a/docs/html/tools/sdk/tools-notes.jd
+++ b/docs/html/tools/sdk/tools-notes.jd
@@ -29,6 +29,42 @@
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>SDK Tools, Revision 22.0.4</a> <em>(July 2013)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+
+ <dl>
+ <dt>Dependencies:</dt>
+ <dd>
+ <ul>
+ <li>Android SDK Platform-tools revision 16 or later.</li>
+ <li>If you are developing in Eclipse with the
+ <a href="{@docRoot}tools/sdk/eclipse-adt.html">ADT Plugin</a>, note that this version of
+ SDK Tools is designed for use with ADT 22.0.4 and later. If you haven't already, update
+ ADT to 22.0.4.</li>
+ <li>If you are using <a href="{@docRoot}sdk/installing/studio.html">Android Studio</a>,
+ note that this version of the SDK Tools is designed to work with Android Studio
+ 0.2.x and later.</li>
+ <li>If you are developing without an integrated development environment (IDE), you must have
+ <a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later.</li>
+ </ul>
+ </dd>
+
+ <dt>General Notes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed problem with compiling Renderscript code.</li>
+ </ul>
+ </dd>
+ </dl>
+ </div>
+</div>
+
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>SDK Tools, Revision 22.0.1</a> <em>(May 2013)</em>
</p>
diff --git a/docs/html/training/implementing-navigation/nav-drawer.jd b/docs/html/training/implementing-navigation/nav-drawer.jd
index 527d570..38b73454 100644
--- a/docs/html/training/implementing-navigation/nav-drawer.jd
+++ b/docs/html/training/implementing-navigation/nav-drawer.jd
@@ -124,6 +124,7 @@
<pre>
public class MainActivity extends Activity {
private String[] mPlanetTitles;
+ private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
...
@@ -133,6 +134,7 @@
setContentView(R.layout.activity_main);
mPlanetTitles = getResources().getStringArray(R.array.planets_array);
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
// Set the adapter for the list view
@@ -191,9 +193,9 @@
.commit();
// Highlight the selected item, update the title, and close the drawer
- mDrawer.setItemChecked(position, true);
+ mDrawerList.setItemChecked(position, true);
setTitle(mPlanetTitles[position]);
- mDrawerLayout.closeDrawer(mDrawer);
+ mDrawerLayout.closeDrawer(mDrawerList);
}
@Override
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 50231da..ad5bfc8 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -170,7 +170,98 @@
public void setDensity(int density) {
mDensity = density;
}
-
+
+ /**
+ * <p>Modifies the bitmap to have a specified width, height, and {@link
+ * Config}, without affecting the underlying allocation backing the bitmap.
+ * Bitmap pixel data is not re-initialized for the new configuration.</p>
+ *
+ * <p>This method can be used to avoid allocating a new bitmap, instead
+ * reusing an existing bitmap's allocation for a new configuration of equal
+ * or lesser size. If the Bitmap's allocation isn't large enough to support
+ * the new configuration, an IllegalArgumentException will be thrown and the
+ * bitmap will not be modified.</p>
+ *
+ * <p>The result of {@link #getByteCount()} will reflect the new configuration,
+ * while {@link #getAllocationByteCount()} will reflect that of the initial
+ * configuration.</p>
+ *
+ * <p>WARNING: This method should NOT be called on a bitmap currently used
+ * by the view system. It does not make guarantees about how the underlying
+ * pixel buffer is remapped to the new config, just that the allocation is
+ * reused. Additionally, the view system does not account for bitmap
+ * properties being modifying during use, e.g. while attached to
+ * drawables.</p>
+ *
+ * @see #setWidth(int)
+ * @see #setHeight(int)
+ * @see #setConfig(Config)
+ */
+ public void reconfigure(int width, int height, Config config) {
+ checkRecycled("Can't call reconfigure() on a recycled bitmap");
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width and height must be > 0");
+ }
+ if (!isMutable()) {
+ throw new IllegalArgumentException("only mutable bitmaps may be reconfigured");
+ }
+ if (mBuffer == null) {
+ throw new IllegalArgumentException("only non-inPurgeable bitmaps may be reconfigured");
+ }
+
+ nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length);
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
+ * with the current height and config.</p>
+ *
+ * <p>WARNING: this method should not be used on bitmaps currently used by
+ * the view system, see {@link #reconfigure(int, int, Config)} for more
+ * details.</p>
+ *
+ * @see #reconfigure(int, int, Config)
+ * @see #setHeight(int)
+ * @see #setConfig(Config)
+ */
+ public void setWidth(int width) {
+ reconfigure(width, getHeight(), getConfig());
+ }
+
+ /**
+ * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
+ * with the current width and config.</p>
+ *
+ * <p>WARNING: this method should not be used on bitmaps currently used by
+ * the view system, see {@link #reconfigure(int, int, Config)} for more
+ * details.</p>
+ *
+ * @see #reconfigure(int, int, Config)
+ * @see #setWidth(int)
+ * @see #setConfig(Config)
+ */
+ public void setHeight(int height) {
+ reconfigure(getWidth(), height, getConfig());
+ }
+
+ /**
+ * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
+ * with the current height and width.</p>
+ *
+ * <p>WARNING: this method should not be used on bitmaps currently used by
+ * the view system, see {@link #reconfigure(int, int, Config)} for more
+ * details.</p>
+ *
+ * @see #reconfigure(int, int, Config)
+ * @see #setWidth(int)
+ * @see #setHeight(int)
+ */
+ public void setConfig(Config config) {
+ reconfigure(getWidth(), getHeight(), config);
+ }
+
/**
* Sets the nine patch chunk.
*
@@ -1010,7 +1101,7 @@
*
* <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, the result of this method can
* no longer be used to determine memory usage of a bitmap. See {@link
- * #getAllocationByteCount()}.
+ * #getAllocationByteCount()}.</p>
*/
public final int getByteCount() {
// int result permits bitmaps up to 46,340 x 46,340
@@ -1021,11 +1112,15 @@
* Returns the size of the allocated memory used to store this bitmap's pixels.
*
* <p>This can be larger than the result of {@link #getByteCount()} if a bitmap is reused to
- * decode other bitmaps of smaller size. See {@link BitmapFactory.Options#inBitmap inBitmap in
- * BitmapFactory.Options}. If a bitmap is not reused in this way, this value will be the same as
- * that returned by {@link #getByteCount()}.
+ * decode other bitmaps of smaller size, or by manual reconfiguration. See {@link
+ * #reconfigure(int, int, Config)}, {@link #setWidth(int)}, {@link #setHeight(int)}, {@link
+ * #setConfig(Bitmap.Config)}, and {@link BitmapFactory.Options#inBitmap
+ * BitmapFactory.Options.inBitmap}. If a bitmap is not modified in this way, this value will be
+ * the same as that returned by {@link #getByteCount()}.</p>
*
- * <p>This value will not change over the lifetime of a Bitmap.
+ * <p>This value will not change over the lifetime of a Bitmap.</p>
+ *
+ * @see #reconfigure(int, int, Config)
*/
public final int getAllocationByteCount() {
return mBuffer.length;
@@ -1423,11 +1518,13 @@
private static native Bitmap nativeCreate(int[] colors, int offset,
int stride, int width, int height,
- int nativeConfig, boolean mutable);
+ int nativeConfig, boolean mutable);
private static native Bitmap nativeCopy(int srcBitmap, int nativeConfig,
boolean isMutable);
private static native void nativeDestructor(int nativeBitmap);
private static native boolean nativeRecycle(int nativeBitmap);
+ private static native void nativeReconfigure(int nativeBitmap, int width, int height,
+ int config, int allocSize);
private static native boolean nativeCompress(int nativeBitmap, int format,
int quality, OutputStream stream,
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index aff4cd8..a4124bf 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -48,34 +48,40 @@
/**
* If set, decode methods that take the Options object will attempt to
- * reuse this bitmap when loading content. If the decode operation cannot
- * use this bitmap, the decode method will return <code>null</code> and
- * will throw an IllegalArgumentException. The current implementation
- * necessitates that the reused bitmap be mutable, and the resulting
- * reused bitmap will continue to remain mutable even when decoding a
- * resource which would normally result in an immutable bitmap.
+ * reuse this bitmap when loading content. If the decode operation
+ * cannot use this bitmap, the decode method will return
+ * <code>null</code> and will throw an IllegalArgumentException. The
+ * current implementation necessitates that the reused bitmap be
+ * mutable, and the resulting reused bitmap will continue to remain
+ * mutable even when decoding a resource which would normally result in
+ * an immutable bitmap.</p>
*
- * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, any mutable
- * bitmap can be reused to decode any other bitmaps as long as the resulting
- * {@link Bitmap#getByteCount() byte count} of the decoded bitmap is less
- * than or equal to the {@link Bitmap#getAllocationByteCount() allocated byte count}
- * of the reused bitmap. This can be because the intrinsic size is smaller,
- * or the size after density / sampled size scaling is smaller.
+ * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, any
+ * mutable bitmap can be reused to decode any other bitmaps as long as
+ * the resulting {@link Bitmap#getByteCount() byte count} of the decoded
+ * bitmap is less than or equal to the {@link
+ * Bitmap#getAllocationByteCount() allocated byte count} of the reused
+ * bitmap. This can be because the intrinsic size is smaller, or its
+ * size post scaling (for density / sample size) is smaller.</p>
*
- * <p>Prior to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE} additional
- * constraints apply: The image being decoded (whether as a resource or
- * as a stream) must be in jpeg or png format. Only equal sized bitmaps
- * are supported, with {@link #inSampleSize} set to 1. Additionally, the
- * {@link android.graphics.Bitmap.Config configuration} of the reused
- * bitmap will override the setting of {@link #inPreferredConfig}, if set.
+ * <p>Prior to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}
+ * additional constraints apply: The image being decoded (whether as a
+ * resource or as a stream) must be in jpeg or png format. Only equal
+ * sized bitmaps are supported, with {@link #inSampleSize} set to 1.
+ * Additionally, the {@link android.graphics.Bitmap.Config
+ * configuration} of the reused bitmap will override the setting of
+ * {@link #inPreferredConfig}, if set.</p>
*
* <p>You should still always use the returned Bitmap of the decode
* method and not assume that reusing the bitmap worked, due to the
* constraints outlined above and failure situations that can occur.
* Checking whether the return value matches the value of the inBitmap
* set in the Options structure will indicate if the bitmap was reused,
- * but in all cases you should use the Bitmap returned by the decoding function to ensure
- * that you are using the bitmap that was used as the decode destination.</p>
+ * but in all cases you should use the Bitmap returned by the decoding
+ * function to ensure that you are using the bitmap that was used as the
+ * decode destination.</p>
+ *
+ * @see Bitmap#reconfigure(int,int,Config)
*/
public Bitmap inBitmap;
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index af1a447..aaed094 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -95,21 +95,6 @@
* @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
*/
public SurfaceTexture(int texName) {
- this(texName, false);
- }
-
- /**
- * Construct a new SurfaceTexture to stream images to a given OpenGL texture.
- *
- * @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
- * @param allowSynchronousMode whether the SurfaceTexture can run in the synchronous mode.
- * When the image stream comes from OpenGL, SurfaceTexture may run in the synchronous
- * mode where the producer side may be blocked to avoid skipping frames. To avoid the
- * thread block, set allowSynchronousMode to false.
- *
- * @hide
- */
- public SurfaceTexture(int texName, boolean allowSynchronousMode) {
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(looper);
@@ -118,7 +103,7 @@
} else {
mEventHandler = null;
}
- nativeInit(texName, new WeakReference<SurfaceTexture>(this), allowSynchronousMode);
+ nativeInit(texName, new WeakReference<SurfaceTexture>(this));
}
/**
@@ -299,7 +284,7 @@
}
}
- private native void nativeInit(int texName, Object weakSelf, boolean allowSynchronousMode);
+ private native void nativeInit(int texName, Object weakSelf);
private native void nativeFinalize();
private native void nativeGetTransformMatrix(float[] mtx);
private native long nativeGetTimestamp();
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 6de8c8c..6ac637e 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -495,6 +495,10 @@
}
}
+void Caches::resetActiveTexture() {
+ mTextureUnit = -1;
+}
+
void Caches::bindTexture(GLuint texture) {
if (mBoundTextures[mTextureUnit] != texture) {
glBindTexture(GL_TEXTURE_2D, texture);
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index b7a97ad..f8b1e17 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -226,6 +226,11 @@
void activeTexture(GLuint textureUnit);
/**
+ * Invalidate the cached value of the active texture unit.
+ */
+ void resetActiveTexture();
+
+ /**
* Binds the specified texture as a GL_TEXTURE_2D texture.
* All texture bindings must be performed with this method or
* bindTexture(GLenum, GLuint).
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 06315ba..bc00ce8 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -360,6 +360,7 @@
mCaches.currentProgram = NULL;
}
}
+ mCaches.resetActiveTexture();
mCaches.unbindMeshBuffer();
mCaches.unbindIndicesBuffer();
mCaches.resetVertexPointers();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3144e8a7..bcd0398 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1659,10 +1659,16 @@
* @see #playSoundEffect(int)
*/
public static final int FX_KEYPRESS_RETURN = 8;
+
+ /**
+ * Invalid keypress sound
+ * @see #playSoundEffect(int)
+ */
+ public static final int FX_KEYPRESS_INVALID = 9;
/**
* @hide Number of sound effects
*/
- public static final int NUM_SOUND_EFFECTS = 9;
+ public static final int NUM_SOUND_EFFECTS = 10;
/**
* Plays a sound effect (Key clicks, lid open/close...)
@@ -1676,6 +1682,7 @@
* {@link #FX_KEYPRESS_SPACEBAR},
* {@link #FX_KEYPRESS_DELETE},
* {@link #FX_KEYPRESS_RETURN},
+ * {@link #FX_KEYPRESS_INVALID},
* NOTE: This version uses the UI settings to determine
* whether sounds are heard or not.
*/
@@ -1708,6 +1715,7 @@
* {@link #FX_KEYPRESS_SPACEBAR},
* {@link #FX_KEYPRESS_DELETE},
* {@link #FX_KEYPRESS_RETURN},
+ * {@link #FX_KEYPRESS_INVALID},
* @param volume Sound effect volume.
* The volume value is a raw scalar so UI controls should be scaled logarithmically.
* If a volume of -1 is specified, the AudioManager.STREAM_MUSIC stream volume minus 3dB will be used.
@@ -1988,7 +1996,7 @@
IAudioService service = getService();
try {
service.requestAudioFocus(streamType, durationHint, mICallBack, null,
- AudioService.IN_VOICE_COMM_FOCUS_ID,
+ MediaFocusControl.IN_VOICE_COMM_FOCUS_ID,
mContext.getBasePackageName());
} catch (RemoteException e) {
Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService due to "+e);
@@ -2004,7 +2012,7 @@
public void abandonAudioFocusForCall() {
IAudioService service = getService();
try {
- service.abandonAudioFocus(null, AudioService.IN_VOICE_COMM_FOCUS_ID);
+ service.abandonAudioFocus(null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID);
} catch (RemoteException e) {
Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService due to "+e);
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 5eb6d37..c178ae4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -22,12 +22,12 @@
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
-import android.app.PendingIntent.OnFinished;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
@@ -106,7 +106,7 @@
*
* @hide
*/
-public class AudioService extends IAudioService.Stub implements OnFinished {
+public class AudioService extends IAudioService.Stub {
private static final String TAG = "AudioService";
@@ -145,34 +145,25 @@
private static final int MSG_BTA2DP_DOCK_TIMEOUT = 7;
private static final int MSG_LOAD_SOUND_EFFECTS = 8;
private static final int MSG_SET_FORCE_USE = 9;
- private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 10;
- private static final int MSG_BT_HEADSET_CNCT_FAILED = 11;
- private static final int MSG_RCDISPLAY_CLEAR = 12;
- private static final int MSG_RCDISPLAY_UPDATE = 13;
- private static final int MSG_SET_ALL_VOLUMES = 14;
- private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
- private static final int MSG_REPORT_NEW_ROUTES = 16;
- private static final int MSG_REEVALUATE_REMOTE = 17;
- private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18;
- private static final int MSG_RCC_NEW_VOLUME_OBS = 19;
- private static final int MSG_SET_FORCE_BT_A2DP_USE = 20;
+ private static final int MSG_BT_HEADSET_CNCT_FAILED = 10;
+ private static final int MSG_SET_ALL_VOLUMES = 11;
+ private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 12;
+ private static final int MSG_REPORT_NEW_ROUTES = 13;
+ private static final int MSG_SET_FORCE_BT_A2DP_USE = 14;
+ private static final int MSG_SET_RSX_CONNECTION_STATE = 15; // change remote submix connection
+ private static final int MSG_CHECK_MUSIC_ACTIVE = 16;
+ private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 17;
+ private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 18;
+ private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 19;
+ private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 20;
+ private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 21;
+ private static final int MSG_UNLOAD_SOUND_EFFECTS = 22;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
- private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21;
- private static final int MSG_SET_A2DP_CONNECTION_STATE = 22;
+ private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 100;
+ private static final int MSG_SET_A2DP_CONNECTION_STATE = 101;
// end of messages handled under wakelock
- private static final int MSG_SET_RSX_CONNECTION_STATE = 23; // change remote submix connection
- private static final int MSG_CHECK_MUSIC_ACTIVE = 24;
- private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 25;
- private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 26;
- private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 27;
- private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 28;
- private static final int MSG_PROMOTE_RCC = 29;
- private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
- private static final int MSG_UNLOAD_SOUND_EFFECTS = 31;
- private static final int MSG_RCC_NEW_PLAYBACK_STATE = 32;
- private static final int MSG_RCC_SEEK_REQUEST = 33;
private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
// Timeout for connection to bluetooth headset service
@@ -186,7 +177,7 @@
private VolumeStreamState[] mStreamStates;
private SettingsObserver mSettingsObserver;
- private int mMode;
+ private int mMode = AudioSystem.MODE_NORMAL;
// protects mRingerMode
private final Object mSettingsLock = new Object();
@@ -213,7 +204,7 @@
private final int[][] SOUND_EFFECT_FILES_MAP = new int[AudioManager.NUM_SOUND_EFFECTS][2];
/** @hide Maximum volume index values for audio streams */
- private final int[] MAX_STREAM_VOLUME = new int[] {
+ private static final int[] MAX_STREAM_VOLUME = new int[] {
5, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING
@@ -345,9 +336,6 @@
// Broadcast receiver for device connections intent broadcasts
private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
- // Used to alter media button redirection when the phone is ringing.
- private boolean mIsRinging = false;
-
// Devices currently connected
private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
@@ -468,6 +456,10 @@
// and used later when/if disableSafeMediaVolume() is called.
private StreamVolumeCommand mPendingVolumeCommand;
+ private PowerManager.WakeLock mAudioEventWakeLock;
+
+ private final MediaFocusControl mMediaFocusControl;
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -481,7 +473,7 @@
com.android.internal.R.bool.config_voice_capable);
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
+ mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
@@ -495,11 +487,13 @@
com.android.internal.R.integer.config_soundEffectVolumeDb);
mVolumePanel = new VolumePanel(context, this);
- mMode = AudioSystem.MODE_NORMAL;
mForcedUseForComm = AudioSystem.FORCE_NONE;
createAudioSystemThread();
+ mMediaFocusControl = new MediaFocusControl(mAudioHandler.getLooper(),
+ mContext, /*VolumeController*/ mVolumePanel, this);
+
boolean cameraSoundForced = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_camera_sound_forced);
mCameraSoundForced = new Boolean(cameraSoundForced);
@@ -528,6 +522,7 @@
updateStreamVolumeAlias(false /*updateVolumes*/);
createStreamStates();
+ readAndSetLowRamDevice();
mMediaServerOk = true;
// Call setRingerModeInt() to apply correct mute
@@ -568,20 +563,6 @@
context.registerReceiver(mReceiver, intentFilter);
- // 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);
-
- // Register for phone state monitoring
- TelephonyManager tmgr = (TelephonyManager)
- context.getSystemService(Context.TELEPHONY_SERVICE);
- tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
-
mUseMasterVolume = context.getResources().getBoolean(
com.android.internal.R.bool.config_useMasterVolume);
restoreMasterVolume();
@@ -589,11 +570,6 @@
mMasterVolumeRamp = context.getResources().getIntArray(
com.android.internal.R.array.config_masterVolumeRamp);
- mMainRemote = new RemotePlaybackState(-1, MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC],
- MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
- mHasRemotePlayback = false;
- mMainRemoteIsActive = false;
- postReevaluateRemote();
}
private void createAudioSystemThread() {
@@ -795,7 +771,7 @@
broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
// Restore the default media button receiver from the system settings
- restoreMediaButtonReceiver();
+ mMediaFocusControl.restoreMediaButtonReceiver();
}
private int rescaleIndex(int index, int srcStream, int dstStream) {
@@ -817,8 +793,8 @@
public void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
String callingPackage) {
if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
- if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
- adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
+ if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
+ mMediaFocusControl.adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0, callingPackage);
}
@@ -847,7 +823,7 @@
// don't play sounds for remote
flags &= ~(AudioManager.FLAG_PLAY_SOUND|AudioManager.FLAG_FIXED_VOLUME);
//if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
- adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
+ mMediaFocusControl.adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
} else {
adjustStreamVolume(streamType, direction, flags, callingPackage);
}
@@ -1274,6 +1250,10 @@
return AudioSystem.getMasterMute();
}
+ protected static int getMaxStreamVolume(int streamType) {
+ return MAX_STREAM_VOLUME[streamType];
+ }
+
/** @see AudioManager#getStreamVolume(int) */
public int getStreamVolume(int streamType) {
ensureValidStreamType(streamType);
@@ -2597,7 +2577,7 @@
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
// Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
// volume can have priority over STREAM_MUSIC
- if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
+ if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
return STREAM_REMOTE_MUSIC;
@@ -2637,7 +2617,7 @@
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
+ if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
// Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
// volume can have priority over STREAM_MUSIC
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
@@ -2681,7 +2661,7 @@
*/
private void queueMsgUnderWakeLock(Handler handler, int msg,
int arg1, int arg2, Object obj, int delay) {
- mMediaEventWakeLock.acquire();
+ mAudioEventWakeLock.acquire();
sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
}
@@ -3379,13 +3359,6 @@
}
}
- private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
- Settings.System.putStringForUser(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER,
- receiver == null ? "" : receiver.flattenToString(),
- UserHandle.USER_CURRENT);
- }
-
private void cleanupPlayer(MediaPlayer mp) {
if (mp != null) {
try {
@@ -3469,6 +3442,8 @@
// process restarts after a crash, not the first time it is started.
AudioSystem.setParameters("restarting=true");
+ readAndSetLowRamDevice();
+
// Restore device connection states
synchronized (mConnectedDevices) {
Set set = mConnectedDevices.entrySet();
@@ -3562,31 +3537,18 @@
setForceUse(msg.arg1, msg.arg2);
break;
- case MSG_PERSIST_MEDIABUTTONRECEIVER:
- onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
- break;
-
- case MSG_RCDISPLAY_CLEAR:
- onRcDisplayClear();
- break;
-
- case MSG_RCDISPLAY_UPDATE:
- // msg.obj is guaranteed to be non null
- onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
- break;
-
case MSG_BT_HEADSET_CNCT_FAILED:
resetBluetoothSco();
break;
case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
- mMediaEventWakeLock.release();
+ mAudioEventWakeLock.release();
break;
case MSG_SET_A2DP_CONNECTION_STATE:
onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
- mMediaEventWakeLock.release();
+ mAudioEventWakeLock.release();
break;
case MSG_REPORT_NEW_ROUTES: {
@@ -3609,26 +3571,6 @@
break;
}
- case MSG_REEVALUATE_REMOTE:
- onReevaluateRemote();
- break;
-
- case MSG_RCC_NEW_PLAYBACK_INFO:
- onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
- ((Integer)msg.obj).intValue() /* value */);
- break;
- case MSG_RCC_NEW_VOLUME_OBS:
- onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
- (IRemoteVolumeObserver)msg.obj /* rvo */);
- break;
- case MSG_RCC_NEW_PLAYBACK_STATE:
- onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
- (RccPlaybackState)msg.obj /* newState */);
- break;
- case MSG_RCC_SEEK_REQUEST:
- onSetRemoteControlClientPlaybackPosition(msg.arg1 /* generationId */,
- ((Long)msg.obj).longValue() /* timeMs */);
-
case MSG_SET_RSX_CONNECTION_STATE:
onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
break;
@@ -3649,10 +3591,6 @@
onPersistSafeVolumeState(msg.arg1);
break;
- case MSG_PROMOTE_RCC:
- onPromoteRcc(msg.arg1);
- break;
-
case MSG_BROADCAST_BT_CONNECTION_STATE:
onBroadcastScoConnectionState(msg.arg1);
break;
@@ -4130,21 +4068,6 @@
0,
null,
SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
- } 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) {
- 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)) {
@@ -4161,7 +4084,7 @@
null,
0);
// the current audio focus owner is no longer valid
- discardAudioFocusOwner();
+ mMediaFocusControl.discardAudioFocusOwner();
// load volume settings for new user
readAudioSettings(true /*userSwitch*/);
@@ -4177,2214 +4100,102 @@
}
//==========================================================================================
- // AudioFocus
+ // RemoteControlDisplay / RemoteControlClient / Remote info
//==========================================================================================
-
- /* constant to identify focus stack entry that is used to hold the focus while the phone
- * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
- * entering and exiting calls.
- */
- public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
-
- private final static Object mAudioFocusLock = new Object();
-
- private final static Object mRingingLock = new Object();
-
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (state == TelephonyManager.CALL_STATE_RINGING) {
- //Log.v(TAG, " CALL_STATE_RINGING");
- synchronized(mRingingLock) {
- mIsRinging = true;
- }
- } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
- || (state == TelephonyManager.CALL_STATE_IDLE)) {
- synchronized(mRingingLock) {
- mIsRinging = false;
- }
- }
- }
- };
-
- /**
- * Discard the current audio focus owner.
- * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
- * focus), remove it from the stack, and clear the remote control display.
- */
- private void discardAudioFocusOwner() {
- synchronized(mAudioFocusLock) {
- if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
- // notify the current focus owner it lost focus after removing it from stack
- FocusStackEntry focusOwner = mFocusStack.pop();
- try {
- focusOwner.mFocusDispatcher.dispatchAudioFocusChange(
- AudioManager.AUDIOFOCUS_LOSS, focusOwner.mClientId);
- } catch (RemoteException e) {
- Log.e(TAG, "Failure to signal loss of audio focus due to "+ e);
- e.printStackTrace();
- }
- focusOwner.unlinkToDeath();
- // clear RCD
- synchronized(mRCStack) {
- clearRemoteControlDisplay_syncAfRcs();
- }
- }
- }
- }
-
- private void notifyTopOfAudioFocusStack() {
- // notify the top of the stack it gained focus
- if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
- if (canReassignAudioFocus()) {
- try {
- mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
- AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
- } catch (RemoteException e) {
- Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
- e.printStackTrace();
- }
- }
- }
- }
-
- private static class FocusStackEntry {
- public int mStreamType = -1;// no stream type
- public IAudioFocusDispatcher mFocusDispatcher = null;
- public IBinder mSourceRef = null;
- public String mClientId;
- public int mFocusChangeType;
- public AudioFocusDeathHandler mHandler;
- public String mPackageName;
- public int mCallingUid;
-
- public FocusStackEntry() {
- }
-
- public FocusStackEntry(int streamType, int duration,
- IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
- String pn, int uid) {
- mStreamType = streamType;
- mFocusDispatcher = afl;
- mSourceRef = source;
- mClientId = id;
- mFocusChangeType = duration;
- mHandler = hdlr;
- mPackageName = pn;
- mCallingUid = uid;
- }
-
- public void unlinkToDeath() {
- try {
- if (mSourceRef != null && mHandler != null) {
- mSourceRef.unlinkToDeath(mHandler, 0);
- mHandler = null;
- }
- } catch (java.util.NoSuchElementException e) {
- Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- unlinkToDeath(); // unlink exception handled inside method
- super.finalize();
- }
- }
-
- private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
-
- /**
- * Helper function:
- * Display in the log the current entries in the audio focus stack
- */
- private void dumpFocusStack(PrintWriter pw) {
- pw.println("\nAudio Focus stack entries (last is top of stack):");
- synchronized(mAudioFocusLock) {
- Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
- while(stackIterator.hasNext()) {
- FocusStackEntry fse = stackIterator.next();
- pw.println(" source:" + fse.mSourceRef
- + " -- pack: " + fse.mPackageName
- + " -- client: " + fse.mClientId
- + " -- duration: " + fse.mFocusChangeType
- + " -- uid: " + fse.mCallingUid
- + " -- stream: " + fse.mStreamType);
- }
- }
- }
-
- /**
- * Helper function:
- * Called synchronized on mAudioFocusLock
- * Remove a focus listener from the focus stack.
- * @param clientToRemove the focus listener
- * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
- * focus, notify the next item in the stack it gained focus.
- */
- private void removeFocusStackEntry(String clientToRemove, boolean signal) {
- // is the current top of the focus stack abandoning focus? (because of request, not death)
- if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
- {
- //Log.i(TAG, " removeFocusStackEntry() removing top of stack");
- FocusStackEntry fse = mFocusStack.pop();
- fse.unlinkToDeath();
- if (signal) {
- // notify the new top of the stack it gained focus
- notifyTopOfAudioFocusStack();
- // there's a new top of the stack, let the remote control know
- synchronized(mRCStack) {
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
- }
- }
- } else {
- // focus is abandoned by a client that's not at the top of the stack,
- // no need to update focus.
- // (using an iterator on the stack so we can safely remove an entry after having
- // evaluated it, traversal order doesn't matter here)
- Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
- while(stackIterator.hasNext()) {
- FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
- if(fse.mClientId.equals(clientToRemove)) {
- Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
- + fse.mClientId);
- stackIterator.remove();
- fse.unlinkToDeath();
- }
- }
- }
- }
-
- /**
- * Helper function:
- * Called synchronized on mAudioFocusLock
- * Remove focus listeners from the focus stack for a particular client when it has died.
- */
- private void removeFocusStackEntryForClient(IBinder cb) {
- // is the owner of the audio focus part of the client to remove?
- boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
- mFocusStack.peek().mSourceRef.equals(cb);
- // (using an iterator on the stack so we can safely remove an entry after having
- // evaluated it, traversal order doesn't matter here)
- Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
- while(stackIterator.hasNext()) {
- FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
- if(fse.mSourceRef.equals(cb)) {
- Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
- + fse.mClientId);
- stackIterator.remove();
- // the client just died, no need to unlink to its death
- }
- }
- if (isTopOfStackForClientToRemove) {
- // we removed an entry at the top of the stack:
- // notify the new top of the stack it gained focus.
- notifyTopOfAudioFocusStack();
- // there's a new top of the stack, let the remote control know
- synchronized(mRCStack) {
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
- }
- }
- }
-
- /**
- * Helper function:
- * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
- */
- private boolean canReassignAudioFocus() {
- // focus requests are rejected during a phone call or when the phone is ringing
- // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
- if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
- return false;
- }
- return true;
- }
-
- /**
- * Inner class to monitor audio focus client deaths, and remove them from the audio focus
- * stack if necessary.
- */
- private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
- private IBinder mCb; // To be notified of client's death
-
- AudioFocusDeathHandler(IBinder cb) {
- mCb = cb;
- }
-
- public void binderDied() {
- synchronized(mAudioFocusLock) {
- Log.w(TAG, " AudioFocus audio focus client died");
- removeFocusStackEntryForClient(mCb);
- }
- }
-
- public IBinder getBinder() {
- return mCb;
- }
- }
-
-
- /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int) */
- public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
- IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
- Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
- // the main stream type for the audio focus request is currently not used. It may
- // potentially be used to handle multiple stream type-dependent audio focuses.
-
- // we need a valid binder callback for clients
- if (!cb.pingBinder()) {
- Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
- return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
- }
-
- if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
- callingPackageName) != AppOpsManager.MODE_ALLOWED) {
- return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
- }
-
- synchronized(mAudioFocusLock) {
- if (!canReassignAudioFocus()) {
- return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
- }
-
- // handle the potential premature death of the new holder of the focus
- // (premature death == death before abandoning focus)
- // Register for client death notification
- AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
- try {
- cb.linkToDeath(afdh, 0);
- } catch (RemoteException e) {
- // client has already died!
- Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
- return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
- }
-
- if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
- // if focus is already owned by this client and the reason for acquiring the focus
- // hasn't changed, don't do anything
- if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
- // unlink death handler so it can be gc'ed.
- // linkToDeath() creates a JNI global reference preventing collection.
- cb.unlinkToDeath(afdh, 0);
- return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
- }
- // the reason for the audio focus request has changed: remove the current top of
- // stack and respond as if we had a new focus owner
- FocusStackEntry fse = mFocusStack.pop();
- fse.unlinkToDeath();
- }
-
- // notify current top of stack it is losing focus
- if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
- try {
- mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
- -1 * focusChangeHint, // loss and gain codes are inverse of each other
- mFocusStack.peek().mClientId);
- } catch (RemoteException e) {
- Log.e(TAG, " Failure to signal loss of focus due to "+ e);
- e.printStackTrace();
- }
- }
-
- // focus requester might already be somewhere below in the stack, remove it
- removeFocusStackEntry(clientId, false /* signal */);
-
- // push focus requester at the top of the audio focus stack
- mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
- clientId, afdh, callingPackageName, Binder.getCallingUid()));
-
- // there's a new top of the stack, let the remote control know
- synchronized(mRCStack) {
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
- }
- }//synchronized(mAudioFocusLock)
-
- return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
- }
-
- /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener) */
- public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
- Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
- try {
- // this will take care of notifying the new focus owner if needed
- synchronized(mAudioFocusLock) {
- removeFocusStackEntry(clientId, true);
- }
- } catch (java.util.ConcurrentModificationException cme) {
- // Catching this exception here is temporary. It is here just to prevent
- // a crash seen when the "Silent" notification is played. This is believed to be fixed
- // but this try catch block is left just to be safe.
- Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme);
- cme.printStackTrace();
- }
-
- return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
- }
-
-
- public void unregisterAudioFocusClient(String clientId) {
- synchronized(mAudioFocusLock) {
- removeFocusStackEntry(clientId, false);
- }
- }
-
-
- //==========================================================================================
- // RemoteControl
- //==========================================================================================
- public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
- }
-
- public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
- filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
- }
-
- private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
- // sanity check on the incoming key event
- if (!isValidMediaKeyEvent(keyEvent)) {
- Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
- return;
- }
- // event filtering for telephony
- synchronized(mRingingLock) {
- synchronized(mRCStack) {
- if ((mMediaReceiverForCalls != null) &&
- (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
- dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
- return;
- }
- }
- }
- // event filtering based on voice-based interactions
- if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
- filterVoiceInputKeyEvent(keyEvent, needWakeLock);
- } else {
- dispatchMediaKeyEvent(keyEvent, needWakeLock);
- }
- }
-
- /**
- * Handles the dispatching of the media button events to the telephony package.
- * Precondition: mMediaReceiverForCalls != null
- * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
- * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
- * is dispatched.
- */
- private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
- Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
- keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
- null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /**
- * Handles the dispatching of the media button events to one of the registered listeners,
- * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
- * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
- * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
- * is dispatched.
- */
- private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- }
- Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
- keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- synchronized(mRCStack) {
- if (!mRCStack.empty()) {
- // send the intent that was registered by the client
- try {
- mRCStack.peek().mMediaIntent.send(mContext,
- needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
- keyIntent, AudioService.this, mAudioHandler);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
- e.printStackTrace();
- }
- } else {
- // legacy behavior when nobody registered their media button event receiver
- // through AudioManager
- if (needWakeLock) {
- keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
- null, mKeyEventDone,
- mAudioHandler, Activity.RESULT_OK, null, null);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
- }
-
- /**
- * The different actions performed in response to a voice button key event.
- */
- private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
- private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
- private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
-
- private final Object mVoiceEventLock = new Object();
- private boolean mVoiceButtonDown;
- private boolean mVoiceButtonHandled;
-
- /**
- * Filter key events that may be used for voice-based interactions
- * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
- * media buttons that can be used to trigger voice-based interactions.
- * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
- * is dispatched.
- */
- private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
- if (DEBUG_RC) {
- Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
- }
-
- int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
- int keyAction = keyEvent.getAction();
- synchronized (mVoiceEventLock) {
- if (keyAction == KeyEvent.ACTION_DOWN) {
- if (keyEvent.getRepeatCount() == 0) {
- // initial down
- mVoiceButtonDown = true;
- mVoiceButtonHandled = false;
- } else if (mVoiceButtonDown && !mVoiceButtonHandled
- && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
- // long-press, start voice-based interactions
- mVoiceButtonHandled = true;
- voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
- }
- } else if (keyAction == KeyEvent.ACTION_UP) {
- if (mVoiceButtonDown) {
- // voice button up
- mVoiceButtonDown = false;
- if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
- voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
- }
- }
- }
- }//synchronized (mVoiceEventLock)
-
- // take action after media button event filtering for voice-based interactions
- switch (voiceButtonAction) {
- case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
- if (DEBUG_RC) Log.v(TAG, " ignore key event");
- break;
- case VOICEBUTTON_ACTION_START_VOICE_INPUT:
- if (DEBUG_RC) Log.v(TAG, " start voice-based interactions");
- // then start the voice-based interactions
- startVoiceBasedInteractions(needWakeLock);
- break;
- case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
- if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock);
- sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
- break;
- }
- }
-
- private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
- // send DOWN event
- KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
- dispatchMediaKeyEvent(keyEvent, needWakeLock);
- // send UP event
- keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
- dispatchMediaKeyEvent(keyEvent, needWakeLock);
-
- }
-
-
- private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
- if (keyEvent == null) {
- return false;
- }
- final int keyCode = keyEvent.getKeyCode();
- switch (keyCode) {
- case KeyEvent.KEYCODE_MUTE:
- case KeyEvent.KEYCODE_HEADSETHOOK:
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- case KeyEvent.KEYCODE_MEDIA_STOP:
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- case KeyEvent.KEYCODE_MEDIA_REWIND:
- case KeyEvent.KEYCODE_MEDIA_RECORD:
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
- case KeyEvent.KEYCODE_MEDIA_CLOSE:
- case KeyEvent.KEYCODE_MEDIA_EJECT:
- case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
- break;
- default:
- return false;
- }
- return true;
- }
-
- /**
- * Checks whether the given key code is one that can trigger the launch of voice-based
- * interactions.
- * @param keyCode the key code associated with the key event
- * @return true if the key is one of the supported voice-based interaction triggers
- */
- private static boolean isValidVoiceInputKeyCode(int keyCode) {
- if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Tell the system to start voice-based interactions / voice commands
- */
- private void startVoiceBasedInteractions(boolean needWakeLock) {
- Intent voiceIntent = null;
- // select which type of search to launch:
- // - screen on and device unlocked: action is ACTION_WEB_SEARCH
- // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
- // with EXTRA_SECURE set to true if the device is securely locked
- PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
- if (!isLocked && pm.isScreenOn()) {
- voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
- Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
- } else {
- voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
- voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
- isLocked && mKeyguardManager.isKeyguardSecure());
- Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
- }
- // start the search activity
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- }
- try {
- if (voiceIntent != null) {
- voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- mContext.startActivity(voiceIntent);
- }
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "No activity for search: " + e);
- } finally {
- if (needWakeLock) {
- mMediaEventWakeLock.release();
- }
- }
- }
-
- private PowerManager.WakeLock mMediaEventWakeLock;
-
- private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
-
- // only set when wakelock was acquired, no need to check value when received
- private static final String EXTRA_WAKELOCK_ACQUIRED =
- "android.media.AudioService.WAKELOCK_ACQUIRED";
-
- public void onSendFinished(PendingIntent pendingIntent, Intent intent,
- int resultCode, String resultData, Bundle resultExtras) {
- if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
- mMediaEventWakeLock.release();
- }
- }
-
- BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- if (intent == null) {
- return;
- }
- Bundle extras = intent.getExtras();
- if (extras == null) {
- return;
- }
- if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
- mMediaEventWakeLock.release();
- }
- }
- };
-
- /**
- * 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.
- * This object may be null.
- * Access protected by mCurrentRcLock.
- */
- private IRemoteControlClient mCurrentRcClient = null;
-
- private final static int RC_INFO_NONE = 0;
- private final static int RC_INFO_ALL =
- RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
- RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
- RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
- RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
-
- /**
- * A monotonically increasing generation counter for mCurrentRcClient.
- * Only accessed with a lock on mCurrentRcLock.
- * No value wrap-around issues as we only act on equal values.
- */
- private int mCurrentRcClientGen = 0;
-
- /**
- * Inner class to monitor remote control client deaths, and remove the client for the
- * remote control stack if necessary.
- */
- private class RcClientDeathHandler implements IBinder.DeathRecipient {
- final private IBinder mCb; // To be notified of client's death
- final private PendingIntent mMediaIntent;
-
- RcClientDeathHandler(IBinder cb, PendingIntent pi) {
- mCb = cb;
- mMediaIntent = pi;
- }
-
- public void binderDied() {
- Log.w(TAG, " RemoteControlClient died");
- // remote control client died, make sure the displays don't use it anymore
- // by setting its remote control client to null
- registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
- // the dead client was maybe handling remote playback, reevaluate
- postReevaluateRemote();
- }
-
- public IBinder getBinder() {
- return mCb;
- }
- }
-
- /**
- * A global counter for RemoteControlClient identifiers
- */
- private static int sLastRccId = 0;
-
- private class RemotePlaybackState {
- int mRccId;
- int mVolume;
- int mVolumeMax;
- int mVolumeHandling;
-
- private RemotePlaybackState(int id, int vol, int volMax) {
- mRccId = id;
- mVolume = vol;
- mVolumeMax = volMax;
- mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
- }
- }
-
- /**
- * Internal cache for the playback information of the RemoteControlClient whose volume gets to
- * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
- * every time we need this info.
- */
- private RemotePlaybackState mMainRemote;
- /**
- * Indicates whether the "main" RemoteControlClient is considered active.
- * Use synchronized on mMainRemote.
- */
- private boolean mMainRemoteIsActive;
- /**
- * Indicates whether there is remote playback going on. True even if there is no "active"
- * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
- * handles remote playback.
- * Use synchronized on mMainRemote.
- */
- private boolean mHasRemotePlayback;
-
- private static class RccPlaybackState {
- public int mState;
- public long mPositionMs;
- public float mSpeed;
-
- public RccPlaybackState(int state, long positionMs, float speed) {
- mState = state;
- mPositionMs = positionMs;
- mSpeed = speed;
- }
-
- public void reset() {
- mState = RemoteControlClient.PLAYSTATE_STOPPED;
- mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
- mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
- }
-
- @Override
- public String toString() {
- return stateToString() + ", "
- + ((mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) ?
- "PLAYBACK_POSITION_INVALID ," : String.valueOf(mPositionMs)) + "ms ,"
- + mSpeed + "X";
- }
-
- private String stateToString() {
- switch (mState) {
- case RemoteControlClient.PLAYSTATE_NONE:
- return "PLAYSTATE_NONE";
- case RemoteControlClient.PLAYSTATE_STOPPED:
- return "PLAYSTATE_STOPPED";
- case RemoteControlClient.PLAYSTATE_PAUSED:
- return "PLAYSTATE_PAUSED";
- case RemoteControlClient.PLAYSTATE_PLAYING:
- return "PLAYSTATE_PLAYING";
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- return "PLAYSTATE_FAST_FORWARDING";
- case RemoteControlClient.PLAYSTATE_REWINDING:
- return "PLAYSTATE_REWINDING";
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- return "PLAYSTATE_SKIPPING_FORWARDS";
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- return "PLAYSTATE_SKIPPING_BACKWARDS";
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- return "PLAYSTATE_BUFFERING";
- case RemoteControlClient.PLAYSTATE_ERROR:
- return "PLAYSTATE_ERROR";
- default:
- return "[invalid playstate]";
- }
- }
- }
-
- private static class RemoteControlStackEntry implements DeathRecipient {
- public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
- final public AudioService mService;
- /**
- * The target for the ACTION_MEDIA_BUTTON events.
- * Always non null.
- */
- final public PendingIntent mMediaIntent;
- /**
- * The registered media button event receiver.
- * Always non null.
- */
- final public ComponentName mReceiverComponent;
- public IBinder mToken;
- public String mCallingPackageName;
- public int mCallingUid;
- /**
- * Provides access to the information to display on the remote control.
- * May be null (when a media button event receiver is registered,
- * but no remote control client has been registered) */
- public IRemoteControlClient mRcClient;
- public RcClientDeathHandler mRcClientDeathHandler;
- /**
- * Information only used for non-local playback
- */
- public int mPlaybackType;
- public int mPlaybackVolume;
- public int mPlaybackVolumeMax;
- public int mPlaybackVolumeHandling;
- public int mPlaybackStream;
- public RccPlaybackState mPlaybackState;
- public IRemoteVolumeObserver mRemoteVolumeObs;
-
- public void resetPlaybackInfo() {
- mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
- mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
- mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
- mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
- mPlaybackStream = AudioManager.STREAM_MUSIC;
- mPlaybackState.reset();
- mRemoteVolumeObs = null;
- }
-
- /** 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;
- mPlaybackState = new RccPlaybackState(
- RemoteControlClient.PLAYSTATE_STOPPED,
- RemoteControlClient.PLAYBACK_POSITION_INVALID,
- 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() {
- if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
- try {
- mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
- mRcClientDeathHandler = null;
- } catch (java.util.NoSuchElementException e) {
- // not much we can do here
- Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
- e.printStackTrace();
- }
- }
- }
-
- 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 {
- destroy(); // unlink exception handled inside method
- super.finalize();
- }
- }
-
- /**
- * The stack of remote control event receivers.
- * Code sections and methods that modify the remote control event receiver stack are
- * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
- * stack, audio focus or RC, can lead to a change in the remote control display
- */
- private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
-
- /**
- * The component the telephony package can register so telephony calls have priority to
- * handle media button events
- */
- private ComponentName mMediaReceiverForCalls = null;
-
- /**
- * Helper function:
- * Display in the log the current entries in the remote control focus stack
- */
- private void dumpRCStack(PrintWriter pw) {
- pw.println("\nRemote Control stack entries (last is top of stack):");
- synchronized(mRCStack) {
- Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry rcse = stackIterator.next();
- pw.println(" pi: " + rcse.mMediaIntent +
- " -- pack: " + rcse.mCallingPackageName +
- " -- ercvr: " + rcse.mReceiverComponent +
- " -- client: " + rcse.mRcClient +
- " -- uid: " + rcse.mCallingUid +
- " -- type: " + rcse.mPlaybackType +
- " state: " + rcse.mPlaybackState);
- }
- }
- }
-
- /**
- * Helper function:
- * Display in the log the current entries in the remote control stack, focusing
- * on RemoteControlClient data
- */
- private void dumpRCCStack(PrintWriter pw) {
- pw.println("\nRemote Control Client stack entries (last is top of stack):");
- synchronized(mRCStack) {
- Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry rcse = stackIterator.next();
- pw.println(" uid: " + rcse.mCallingUid +
- " -- id: " + rcse.mRccId +
- " -- type: " + rcse.mPlaybackType +
- " -- state: " + rcse.mPlaybackState +
- " -- vol handling: " + rcse.mPlaybackVolumeHandling +
- " -- vol: " + rcse.mPlaybackVolume +
- " -- volMax: " + rcse.mPlaybackVolumeMax +
- " -- volObs: " + rcse.mRemoteVolumeObs);
- }
- synchronized(mCurrentRcLock) {
- pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
- }
- }
- synchronized (mMainRemote) {
- pw.println("\nRemote Volume State:");
- pw.println(" has remote: " + mHasRemotePlayback);
- pw.println(" is remote active: " + mMainRemoteIsActive);
- pw.println(" rccId: " + mMainRemote.mRccId);
- pw.println(" volume handling: "
- + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
- "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
- pw.println(" volume: " + mMainRemote.mVolume);
- pw.println(" volume steps: " + mMainRemote.mVolumeMax);
- }
- }
-
- /**
- * Helper function:
- * Display in the log the current entries in the list of remote control displays
- */
- private void dumpRCDList(PrintWriter pw) {
- pw.println("\nRemote Control Display list entries:");
- synchronized(mRCStack) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
- pw.println(" IRCD: " + di.mRcDisplay +
- " -- w:" + di.mArtworkExpectedWidth +
- " -- h:" + di.mArtworkExpectedHeight+
- " -- wantsPosSync:" + di.mWantsPositionSync);
- }
- }
- }
-
- /**
- * Helper function:
- * Remove any entry in the remote control stack that has the same package name as packageName
- * Pre-condition: packageName != null
- */
- 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
- // (using an iterator on the stack so we can safely remove an entry after having
- // evaluated it, traversal order doesn't matter here)
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
- if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
- // a stack entry is from the package being removed, remove it from the stack
- stackIterator.remove();
- 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()) {
- // no saved media button receiver
- mAudioHandler.sendMessage(
- mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
- null));
- } else if (oldTop != mRCStack.peek()) {
- // the top of the stack has changed, save it in the system settings
- // by posting a message to persist it; only do this however if it has
- // a concrete component name (is not a transient registration)
- RemoteControlStackEntry rcse = mRCStack.peek();
- if (rcse.mReceiverComponent != null) {
- mAudioHandler.sendMessage(
- mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
- rcse.mReceiverComponent));
- }
- }
- }
- }
- }
-
- /**
- * Helper function:
- * Restore remote control receiver from the system settings.
- */
- private void restoreMediaButtonReceiver() {
- String receiverName = Settings.System.getStringForUser(mContentResolver,
- 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);
- // the associated intent will be handled by the component being registered
- mediaButtonIntent.setComponent(eventReceiver);
- PendingIntent pi = PendingIntent.getBroadcast(mContext,
- 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
- registerMediaButtonIntent(pi, eventReceiver, null);
- }
- }
-
- /**
- * 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
- */
- private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
- IBinder token) {
- // already at top of stack?
- if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
- return;
- }
- if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(),
- mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) {
- return;
- }
- RemoteControlStackEntry rcse = null;
- boolean wasInsideStack = false;
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- rcse = mRCStack.elementAt(index);
- if(rcse.mMediaIntent.equals(mediaIntent)) {
- // ok to remove element while traversing the stack since we're leaving the loop
- mRCStack.removeElementAt(index);
- wasInsideStack = true;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- if (!wasInsideStack) {
- rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
- }
- mRCStack.push(rcse); // rcse is never null
-
- // post message to persist the default media button receiver
- if (target != null) {
- mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
- MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
- }
- }
-
- /**
- * Helper function:
- * Remove the remote control receiver from the RC focus stack.
- * Called synchronized on mAudioFocusLock, then mRCStack
- * precondition: pi != null
- */
- private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) {
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- if (rcse.mMediaIntent.equals(pi)) {
- rcse.destroy();
- // ok to remove element while traversing the stack since we're leaving the loop
- mRCStack.removeElementAt(index);
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
-
- /**
- * Helper function:
- * Called synchronized on mRCStack
- */
- private boolean isCurrentRcController(PendingIntent pi) {
- if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
- return true;
- }
- return false;
- }
-
- //==========================================================================================
- // Remote control display / client
- //==========================================================================================
- /**
- * Update the remote control displays with the new "focused" client generation
- */
- private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
- PendingIntent newMediaIntent, boolean clearing) {
- synchronized(mRCStack) {
- if (mRcDisplays.size() > 0) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- try {
- di.mRcDisplay.setCurrentClientId(
- newClientGeneration, newMediaIntent, clearing);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
- di.release();
- displayIterator.remove();
- }
- }
- }
- }
- }
-
- /**
- * Update the remote control clients with the new "focused" client generation
- */
- private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
- // (using an iterator on the stack so we can safely remove an entry if needed,
- // traversal order doesn't matter here as we update all entries)
- Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry se = stackIterator.next();
- if ((se != null) && (se.mRcClient != null)) {
- try {
- se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
- } catch (RemoteException e) {
- Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
- stackIterator.remove();
- se.unlinkToRcClientDeath();
- }
- }
- }
- }
-
- /**
- * Update the displays and clients with the new "focused" client generation and name
- * @param newClientGeneration the new generation value matching a client update
- * @param newMediaIntent the media button event receiver associated with the client.
- * May be null, which implies there is no registered media button event receiver.
- * @param clearing true if the new client generation value maps to a remote control update
- * where the display should be cleared.
- */
- private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
- PendingIntent newMediaIntent, boolean clearing) {
- // send the new valid client generation ID to all displays
- setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
- // send the new valid client generation ID to all clients
- setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
- }
-
- /**
- * Called when processing MSG_RCDISPLAY_CLEAR event
- */
- private void onRcDisplayClear() {
- if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
-
- synchronized(mRCStack) {
- synchronized(mCurrentRcLock) {
- mCurrentRcClientGen++;
- // synchronously update the displays and clients with the new client generation
- setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
- null /*newMediaIntent*/, true /*clearing*/);
- }
- }
- }
-
- /**
- * Called when processing MSG_RCDISPLAY_UPDATE event
- */
- private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
- synchronized(mRCStack) {
- synchronized(mCurrentRcLock) {
- if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
- if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
-
- mCurrentRcClientGen++;
- // synchronously update the displays and clients with
- // the new client generation
- setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
- rcse.mMediaIntent /*newMediaIntent*/,
- false /*clearing*/);
-
- // tell the current client that it needs to send info
- try {
- mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
- } catch (RemoteException e) {
- Log.e(TAG, "Current valid remote client is dead: "+e);
- mCurrentRcClient = null;
- }
- } else {
- // the remote control display owner has changed between the
- // the message to update the display was sent, and the time it
- // gets to be processed (now)
- }
- }
- }
- }
-
-
- /**
- * Helper function:
- * Called synchronized on mRCStack
- */
- private void clearRemoteControlDisplay_syncAfRcs() {
- synchronized(mCurrentRcLock) {
- mCurrentRcClient = null;
- }
- // will cause onRcDisplayClear() to be called in AudioService's handler thread
- mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
- }
-
- /**
- * Helper function for code readability: only to be called from
- * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
- * this method.
- * Preconditions:
- * - called synchronized mAudioFocusLock then on mRCStack
- * - mRCStack.isEmpty() is false
- */
- private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
- RemoteControlStackEntry rcse = mRCStack.peek();
- int infoFlagsAboutToBeUsed = infoChangedFlags;
- // this is where we enforce opt-in for information display on the remote controls
- // with the new AudioManager.registerRemoteControlClient() API
- if (rcse.mRcClient == null) {
- //Log.w(TAG, "Can't update remote control display with null remote control client");
- clearRemoteControlDisplay_syncAfRcs();
- return;
- }
- synchronized(mCurrentRcLock) {
- if (!rcse.mRcClient.equals(mCurrentRcClient)) {
- // new RC client, assume every type of information shall be queried
- infoFlagsAboutToBeUsed = RC_INFO_ALL;
- }
- mCurrentRcClient = rcse.mRcClient;
- }
- // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
- mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
- infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
- }
-
- /**
- * Helper function:
- * Called synchronized on mAudioFocusLock, then mRCStack
- * Check whether the remote control display should be updated, triggers the update if required
- * @param infoChangedFlags the flags corresponding to the remote control client information
- * that has changed, if applicable (checking for the update conditions might trigger a
- * clear, rather than an update event).
- */
- private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
- // determine whether the remote control display should be refreshed
- // if either stack is empty, there is a mismatch, so clear the RC display
- if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
- clearRemoteControlDisplay_syncAfRcs();
- return;
- }
-
- // determine which entry in the AudioFocus stack to consider, and compare against the
- // top of the stack for the media button event receivers : simply using the top of the
- // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
- // notifications playing during music playback.
- // Crawl the AudioFocus stack from the top until an entry is found with the following
- // characteristics:
- // - focus gain on STREAM_MUSIC stream
- // - non-transient focus gain on a stream other than music
- FocusStackEntry af = null;
- try {
- for (int index = mFocusStack.size()-1; index >= 0; index--) {
- FocusStackEntry fse = mFocusStack.elementAt(index);
- if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
- || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
- af = fse;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
- af = null;
- }
- if (af == null) {
- clearRemoteControlDisplay_syncAfRcs();
- return;
- }
-
- // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
- if ((mRCStack.peek().mCallingPackageName != null)
- && (af.mPackageName != null)
- && !(mRCStack.peek().mCallingPackageName.compareTo(
- af.mPackageName) == 0)) {
- clearRemoteControlDisplay_syncAfRcs();
- return;
- }
- // if the audio focus didn't originate from the same Uid as the one in which the remote
- // control information will be retrieved, clear
- if (mRCStack.peek().mCallingUid != af.mCallingUid) {
- clearRemoteControlDisplay_syncAfRcs();
- return;
- }
-
- // refresh conditions were verified: update the remote controls
- // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
- updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
- }
-
- /**
- * Helper function:
- * Post a message to asynchronously move the media button event receiver associated with the
- * given remote control client ID to the top of the remote control stack
- * @param rccId
- */
- private void postPromoteRcc(int rccId) {
- sendMsg(mAudioHandler, MSG_PROMOTE_RCC, SENDMSG_REPLACE,
- rccId /*arg1*/, 0, null, 0/*delay*/);
- }
-
- private void onPromoteRcc(int rccId) {
- if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); }
- synchronized(mAudioFocusLock) {
- synchronized(mRCStack) {
- // ignore if given RCC ID is already at top of remote control stack
- if (!mRCStack.isEmpty() && (mRCStack.peek().mRccId == rccId)) {
- return;
- }
- int indexToPromote = -1;
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- if (rcse.mRccId == rccId) {
- indexToPromote = index;
- break;
- }
- }
- if (indexToPromote >= 0) {
- if (DEBUG_RC) { Log.d(TAG, " moving RCC from index " + indexToPromote
- + " to " + (mRCStack.size()-1)); }
- final RemoteControlStackEntry rcse = mRCStack.remove(indexToPromote);
- mRCStack.push(rcse);
- // the RC stack changed, reevaluate the display
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
- }//synchronized(mRCStack)
- }//synchronized(mAudioFocusLock)
- }
-
- /**
- * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
- * precondition: mediaIntent != null
- */
- 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, token);
- // new RC client, assume every type of information shall be queried
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
- }
- }
- }
-
- /**
- * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
- * precondition: mediaIntent != null, eventReceiver != null
- */
- public void unregisterMediaButtonIntent(PendingIntent mediaIntent)
- {
- Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent);
-
- synchronized(mAudioFocusLock) {
- synchronized(mRCStack) {
- boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
- removeMediaButtonReceiver_syncAfRcs(mediaIntent);
- if (topOfStackWillChange) {
- // current RC client will change, assume every type of info needs to be queried
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
- }
- }
- }
- }
-
- /**
- * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
- * precondition: c != null
- */
- public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
- if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
- != PackageManager.PERMISSION_GRANTED) {
- Log.e(TAG, "Invalid permissions to register media button receiver for calls");
- return;
- }
- synchronized(mRCStack) {
- mMediaReceiverForCalls = c;
- }
- }
-
- /**
- * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
- */
- public void unregisterMediaButtonEventReceiverForCalls() {
- if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
- != PackageManager.PERMISSION_GRANTED) {
- Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
- return;
- }
- synchronized(mRCStack) {
- mMediaReceiverForCalls = null;
- }
- }
-
- /**
- * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
- * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
- * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
- * without modifying the RC stack, but while still causing the display to refresh (will
- * become blank as a result of this)
- */
- public int registerRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient, String callingPackageName) {
- if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
- int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
- synchronized(mAudioFocusLock) {
- synchronized(mRCStack) {
- // store the new display information
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- if(rcse.mMediaIntent.equals(mediaIntent)) {
- // already had a remote control client?
- if (rcse.mRcClientDeathHandler != null) {
- // stop monitoring the old client's death
- rcse.unlinkToRcClientDeath();
- }
- // save the new remote control client
- rcse.mRcClient = rcClient;
- rcse.mCallingPackageName = callingPackageName;
- rcse.mCallingUid = Binder.getCallingUid();
- if (rcClient == null) {
- // here rcse.mRcClientDeathHandler is null;
- rcse.resetPlaybackInfo();
- break;
- }
- rccId = rcse.mRccId;
-
- // there is a new (non-null) client:
- // 1/ give the new client the displays (if any)
- if (mRcDisplays.size() > 0) {
- plugRemoteControlDisplaysIntoClient_syncRcStack(rcse.mRcClient);
- }
- // 2/ monitor the new client's death
- IBinder b = rcse.mRcClient.asBinder();
- RcClientDeathHandler rcdh =
- new RcClientDeathHandler(b, rcse.mMediaIntent);
- try {
- b.linkToDeath(rcdh, 0);
- } catch (RemoteException e) {
- // remote control client is DOA, disqualify it
- Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
- rcse.mRcClient = null;
- }
- rcse.mRcClientDeathHandler = rcdh;
- break;
- }
- }//for
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
-
- // if the eventReceiver is at the top of the stack
- // then check for potential refresh of the remote controls
- if (isCurrentRcController(mediaIntent)) {
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
- }
- }//synchronized(mRCStack)
- }//synchronized(mAudioFocusLock)
- return rccId;
- }
-
- /**
- * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
- * rcClient is guaranteed non-null
- */
- public void unregisterRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient) {
- if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
- synchronized(mAudioFocusLock) {
- synchronized(mRCStack) {
- boolean topRccChange = false;
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- if ((rcse.mMediaIntent.equals(mediaIntent))
- && rcClient.equals(rcse.mRcClient)) {
- // we found the IRemoteControlClient to unregister
- // stop monitoring its death
- rcse.unlinkToRcClientDeath();
- // reset the client-related fields
- rcse.mRcClient = null;
- rcse.mCallingPackageName = null;
- topRccChange = (index == mRCStack.size()-1);
- // there can only be one matching RCC in the RC stack, we're done
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
- if (topRccChange) {
- // no more RCC for the RCD, check for potential refresh of the remote controls
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
- }
- }
- }
- }
-
-
- /**
- * A class to encapsulate all the information about a remote control display.
- * After instanciation, init() must always be called before the object is added in the list
- * of displays.
- * Before being removed from the list of displays, release() must always be called (otherwise
- * it will leak death handlers).
- */
- private class DisplayInfoForServer implements IBinder.DeathRecipient {
- /** may never be null */
- private IRemoteControlDisplay mRcDisplay;
- private IBinder mRcDisplayBinder;
- private int mArtworkExpectedWidth = -1;
- private int mArtworkExpectedHeight = -1;
- private boolean mWantsPositionSync = false;
-
- public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
- if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
- mRcDisplay = rcd;
- mRcDisplayBinder = rcd.asBinder();
- mArtworkExpectedWidth = w;
- mArtworkExpectedHeight = h;
- }
-
- public boolean init() {
- try {
- mRcDisplayBinder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- // remote control display is DOA, disqualify it
- Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
- return false;
- }
- return true;
- }
-
- public void release() {
- try {
- mRcDisplayBinder.unlinkToDeath(this, 0);
- } catch (java.util.NoSuchElementException e) {
- // not much we can do here, the display should have been unregistered anyway
- Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
- }
- }
-
- public void binderDied() {
- synchronized(mRCStack) {
- Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
- // remove the display from the list
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
- if (di.mRcDisplay == mRcDisplay) {
- if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
- displayIterator.remove();
- return;
- }
- }
- }
- }
- }
-
- /**
- * The remote control displays.
- * Access synchronized on mRCStack
- */
- private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
-
- /**
- * Plug each registered display into the specified client
- * @param rcc, guaranteed non null
- */
- private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
- try {
- rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
- di.mArtworkExpectedHeight);
- if (di.mWantsPositionSync) {
- rcc.setWantsSyncForDisplay(di.mRcDisplay, true);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
- }
- }
- }
-
- /**
- * Is the remote control display interface already registered
- * @param rcd
- * @return true if the IRemoteControlDisplay is already in the list of displays
- */
- private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Register an IRemoteControlDisplay.
- * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
- * at the top of the stack to update the new display with its information.
- * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
- * @param rcd the IRemoteControlDisplay to register. No effect if null.
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- */
public void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
- if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
- synchronized(mAudioFocusLock) {
- synchronized(mRCStack) {
- if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
- return;
- }
- DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
- if (!di.init()) {
- if (DEBUG_RC) Log.e(TAG, " error registering RCD");
- return;
- }
- // add RCD to list of displays
- mRcDisplays.add(di);
-
- // let all the remote control clients know there is a new display (so the remote
- // control stack traversal order doesn't matter).
- Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry rcse = stackIterator.next();
- if(rcse.mRcClient != null) {
- try {
- rcse.mRcClient.plugRemoteControlDisplay(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Error connecting RCD to client: ", e);
- }
- }
- }
-
- // we have a new display, of which all the clients are now aware: have it be updated
- checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
- }
- }
+ mMediaFocusControl.registerRemoteControlDisplay(rcd, w, h);
}
- /**
- * Unregister an IRemoteControlDisplay.
- * No effect if the IRemoteControlDisplay hasn't been successfully registered.
- * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
- * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
- */
public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
- if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
- synchronized(mRCStack) {
- if (rcd == null) {
- return;
- }
-
- boolean displayWasPluggedIn = false;
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext() && !displayWasPluggedIn) {
- final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- displayWasPluggedIn = true;
- di.release();
- displayIterator.remove();
- }
- }
-
- if (displayWasPluggedIn) {
- // disconnect this remote control display from all the clients, so the remote
- // control stack traversal order doesn't matter
- final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- final RemoteControlStackEntry rcse = stackIterator.next();
- if(rcse.mRcClient != null) {
- try {
- rcse.mRcClient.unplugRemoteControlDisplay(rcd);
- } catch (RemoteException e) {
- Log.e(TAG, "Error disconnecting remote control display to client: ", e);
- }
- }
- }
- } else {
- if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD");
- }
- }
+ mMediaFocusControl.unregisterRemoteControlDisplay(rcd);
}
- /**
- * Update the size of the artwork used by an IRemoteControlDisplay.
- * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
- * @param rcd the IRemoteControlDisplay with the new artwork size requirement
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- */
public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
- synchronized(mRCStack) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- boolean artworkSizeUpdate = false;
- while (displayIterator.hasNext() && !artworkSizeUpdate) {
- final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
- di.mArtworkExpectedWidth = w;
- di.mArtworkExpectedHeight = h;
- artworkSizeUpdate = true;
- }
- }
- }
- if (artworkSizeUpdate) {
- // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
- // stack traversal order doesn't matter
- final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- final RemoteControlStackEntry rcse = stackIterator.next();
- if(rcse.mRcClient != null) {
- try {
- rcse.mRcClient.setBitmapSizeForDisplay(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
- }
- }
- }
- }
- }
+ mMediaFocusControl.remoteControlDisplayUsesBitmapSize(rcd, w, h);
}
- /**
- * Controls whether a remote control display needs periodic checks of the RemoteControlClient
- * playback position to verify that the estimated position has not drifted from the actual
- * position. By default the check is not performed.
- * The IRemoteControlDisplay must have been previously registered for this to have any effect.
- * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
- * or disabled. Not null.
- * @param wantsSync if true, RemoteControlClient instances which expose their playback position
- * to the framework will regularly compare the estimated playback position with the actual
- * position, and will update the IRemoteControlDisplay implementation whenever a drift is
- * detected.
- */
public void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
boolean wantsSync) {
- synchronized(mRCStack) {
- boolean rcdRegistered = false;
- // store the information about this display
- // (display stack traversal order doesn't matter).
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- di.mWantsPositionSync = wantsSync;
- rcdRegistered = true;
- break;
- }
- }
- if (!rcdRegistered) {
- return;
- }
- // notify all current RemoteControlClients
- // (stack traversal order doesn't matter as we notify all RCCs)
- final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while (stackIterator.hasNext()) {
- final RemoteControlStackEntry rcse = stackIterator.next();
- if (rcse.mRcClient != null) {
- try {
- rcse.mRcClient.setWantsSyncForDisplay(rcd, wantsSync);
- } catch (RemoteException e) {
- Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e);
- }
- }
- }
- }
+ mMediaFocusControl.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
+ }
+
+ public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
+ mMediaFocusControl.registerMediaButtonEventReceiverForCalls(c);
+ }
+
+ public void unregisterMediaButtonEventReceiverForCalls() {
+ mMediaFocusControl.unregisterMediaButtonEventReceiverForCalls();
+ }
+
+ public void registerMediaButtonIntent(PendingIntent pi, ComponentName c, IBinder token) {
+ mMediaFocusControl.registerMediaButtonIntent(pi, c, token);
+ }
+
+ public void unregisterMediaButtonIntent(PendingIntent pi) {
+ mMediaFocusControl.unregisterMediaButtonIntent(pi);
+ }
+
+ public int registerRemoteControlClient(PendingIntent mediaIntent,
+ IRemoteControlClient rcClient, String callingPckg) {
+ return mMediaFocusControl.registerRemoteControlClient(mediaIntent, rcClient, callingPckg);
+ }
+
+ public void unregisterRemoteControlClient(PendingIntent mediaIntent,
+ IRemoteControlClient rcClient) {
+ mMediaFocusControl.unregisterRemoteControlClient(mediaIntent, rcClient);
}
public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- // ignore position change requests if invalid generation ID
- synchronized(mRCStack) {
- synchronized(mCurrentRcLock) {
- if (mCurrentRcClientGen != generationId) {
- return;
- }
- }
- }
- // discard any unprocessed seek request in the message queue, and replace with latest
- sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, 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 */);
- }
-
- // handler for MSG_RCC_NEW_PLAYBACK_INFO
- private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
- if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
- ", what=" + key + ",val=" + value + ")");
- synchronized(mRCStack) {
- // iterating from top of stack as playback information changes are more likely
- // on entries at the top of the remote control stack
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- if (rcse.mRccId == rccId) {
- switch (key) {
- case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
- rcse.mPlaybackType = value;
- postReevaluateRemote();
- break;
- case RemoteControlClient.PLAYBACKINFO_VOLUME:
- rcse.mPlaybackVolume = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemote.mVolume = value;
- mVolumePanel.postHasNewRemotePlaybackInfo();
- }
- }
- break;
- case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
- rcse.mPlaybackVolumeMax = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemote.mVolumeMax = value;
- mVolumePanel.postHasNewRemotePlaybackInfo();
- }
- }
- break;
- case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
- rcse.mPlaybackVolumeHandling = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemote.mVolumeHandling = value;
- mVolumePanel.postHasNewRemotePlaybackInfo();
- }
- }
- break;
- case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
- rcse.mPlaybackStream = value;
- break;
- default:
- Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
- break;
- }
- return;
- }
- }//for
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e);
- }
- }
- }
-
- public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
- sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
- rccId /* arg1 */, state /* arg2 */,
- new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
- }
-
- public void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
- if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
- + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
- synchronized(mRCStack) {
- // iterating from top of stack as playback information changes are more likely
- // on entries at the top of the remote control stack
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- if (rcse.mRccId == rccId) {
- rcse.mPlaybackState = newState;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemoteIsActive = isPlaystateActive(state);
- postReevaluateRemote();
- }
- }
- // an RCC moving to a "playing" state should become the media button
- // event receiver so it can be controlled, without requiring the
- // app to re-register its receiver
- if (isPlaystateActive(state)) {
- postPromoteRcc(rccId);
- }
- }
- }//for
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e);
- }
- }
+ mMediaFocusControl.setRemoteControlClientPlaybackPosition(generationId, timeMs);
}
public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
- sendMsg(mAudioHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
- rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
- }
-
- // handler for MSG_RCC_NEW_VOLUME_OBS
- private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
- synchronized(mRCStack) {
- // The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, but the matching ID is more likely at the top of the stack, so
- // start iterating from the top.
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- if (rcse.mRccId == rccId) {
- rcse.mRemoteVolumeObs = rvo;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
- }
-
- /**
- * Checks if a remote client is active on the supplied stream type. Update the remote stream
- * volume state if found and playing
- * @param streamType
- * @return false if no remote playing is currently playing
- */
- private boolean checkUpdateRemoteStateIfActive(int streamType) {
- synchronized(mRCStack) {
- // iterating from top of stack as active playback is more likely on entries at the top
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
- && isPlaystateActive(rcse.mPlaybackState.mState)
- && (rcse.mPlaybackStream == streamType)) {
- if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
- + ", vol =" + rcse.mPlaybackVolume);
- synchronized (mMainRemote) {
- mMainRemote.mRccId = rcse.mRccId;
- mMainRemote.mVolume = rcse.mPlaybackVolume;
- mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
- mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
- mMainRemoteIsActive = true;
- }
- return true;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
- }
- synchronized (mMainRemote) {
- mMainRemoteIsActive = false;
- }
- return false;
- }
-
- /**
- * Returns true if the given playback state is considered "active", i.e. it describes a state
- * where playback is happening, or about to
- * @param playState the playback state to evaluate
- * @return true if active, false otherwise (inactive or unknown)
- */
- private static boolean isPlaystateActive(int playState) {
- switch (playState) {
- case RemoteControlClient.PLAYSTATE_PLAYING:
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- case RemoteControlClient.PLAYSTATE_REWINDING:
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- return true;
- default:
- return false;
- }
- }
-
- private void adjustRemoteVolume(int streamType, int direction, int flags) {
- int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
- boolean volFixed = false;
- synchronized (mMainRemote) {
- if (!mMainRemoteIsActive) {
- if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
- return;
- }
- rccId = mMainRemote.mRccId;
- volFixed = (mMainRemote.mVolumeHandling ==
- RemoteControlClient.PLAYBACK_VOLUME_FIXED);
- }
- // unlike "local" stream volumes, we can't compute the new volume based on the direction,
- // we can only notify the remote that volume needs to be updated, and we'll get an async'
- // update through setPlaybackInfoForRcc()
- if (!volFixed) {
- sendVolumeUpdateToRemote(rccId, direction);
- }
-
- // fire up the UI
- mVolumePanel.postRemoteVolumeChanged(streamType, flags);
- }
-
- private void sendVolumeUpdateToRemote(int rccId, int direction) {
- if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
- if (direction == 0) {
- // only handling discrete events
- return;
- }
- IRemoteVolumeObserver rvo = null;
- synchronized (mRCStack) {
- // The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, but the matching ID is more likely at the top of the stack, so
- // start iterating from the top.
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
- if (rcse.mRccId == rccId) {
- rvo = rcse.mRemoteVolumeObs;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
- if (rvo != null) {
- try {
- rvo.dispatchRemoteVolumeUpdate(direction, -1);
- } catch (RemoteException e) {
- Log.e(TAG, "Error dispatching relative volume update", e);
- }
- }
- }
-
- public int getRemoteStreamMaxVolume() {
- synchronized (mMainRemote) {
- if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
- return 0;
- }
- return mMainRemote.mVolumeMax;
- }
+ mMediaFocusControl.registerRemoteVolumeObserverForRcc(rccId, rvo);
}
public int getRemoteStreamVolume() {
- synchronized (mMainRemote) {
- if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
- return 0;
- }
- return mMainRemote.mVolume;
- }
+ return mMediaFocusControl.getRemoteStreamVolume();
}
- public void setRemoteStreamVolume(int vol) {
- if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
- int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
- synchronized (mMainRemote) {
- if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
- return;
- }
- rccId = mMainRemote.mRccId;
- }
- IRemoteVolumeObserver rvo = null;
- synchronized (mRCStack) {
- // The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, but the matching ID is more likely at the top of the stack, so
- // start iterating from the top.
- try {
- for (int index = mRCStack.size()-1; index >= 0; index--) {
- final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
- //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
- if (rcse.mRccId == rccId) {
- rvo = rcse.mRemoteVolumeObs;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
- if (rvo != null) {
- try {
- rvo.dispatchRemoteVolumeUpdate(0, vol);
- } catch (RemoteException e) {
- Log.e(TAG, "Error dispatching absolute volume update", e);
- }
- }
+ public int getRemoteStreamMaxVolume() {
+ return mMediaFocusControl.getRemoteStreamMaxVolume();
}
- /**
- * Call to make AudioService reevaluate whether it's in a mode where remote players should
- * have their volume controlled. In this implementation this is only to reset whether
- * VolumePanel should display remote volumes
- */
- private void postReevaluateRemote() {
- sendMsg(mAudioHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
+ public void setRemoteStreamVolume(int index) {
+ mMediaFocusControl.setRemoteStreamVolume(index);
}
- private void onReevaluateRemote() {
- if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
- // is there a registered RemoteControlClient that is handling remote playback
- boolean hasRemotePlayback = false;
- synchronized (mRCStack) {
- // iteration stops when PLAYBACK_TYPE_REMOTE is found, so remote control stack
- // traversal order doesn't matter
- Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry rcse = stackIterator.next();
- if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
- hasRemotePlayback = true;
- break;
- }
- }
- }
- synchronized (mMainRemote) {
- if (mHasRemotePlayback != hasRemotePlayback) {
- mHasRemotePlayback = hasRemotePlayback;
- mVolumePanel.postRemoteSliderVisibility(hasRemotePlayback);
- }
- }
+ public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
+ mMediaFocusControl.setPlaybackStateForRcc(rccId, state, timeMs, speed);
+ }
+
+ public void setPlaybackInfoForRcc(int rccId, int what, int value) {
+ mMediaFocusControl.setPlaybackInfoForRcc(rccId, what, value);
+ }
+
+ public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
+ mMediaFocusControl.dispatchMediaKeyEvent(keyEvent);
+ }
+
+ public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
+ mMediaFocusControl.dispatchMediaKeyEventUnderWakelock(keyEvent);
+ }
+
+ //==========================================================================================
+ // Audio Focus
+ //==========================================================================================
+ public int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb,
+ IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
+ return mMediaFocusControl.requestAudioFocus(mainStreamType, durationHint, cb, fd,
+ clientId, callingPackageName);
+ }
+
+ public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId) {
+ return mMediaFocusControl.abandonAudioFocus(fd, clientId);
+ }
+
+ public void unregisterAudioFocusClient(String clientId) {
+ mMediaFocusControl.unregisterAudioFocusClient(clientId);
}
//==========================================================================================
@@ -6688,14 +4499,20 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
- dumpFocusStack(pw);
- dumpRCStack(pw);
- dumpRCCStack(pw);
- dumpRCDList(pw);
+ mMediaFocusControl.dump(pw);
dumpStreamStates(pw);
dumpRingerMode(pw);
pw.println("\nAudio routes:");
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
pw.print(" mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
}
+
+ // Inform AudioFlinger of our device's low RAM attribute
+ private static void readAndSetLowRamDevice()
+ {
+ int status = AudioSystem.setLowRamDevice(ActivityManager.isLowRamDeviceStatic());
+ if (status != 0) {
+ Log.w(TAG, "AudioFlinger informed of device's low RAM attribute; status " + status);
+ }
+ }
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index d42bfd44..4805da5 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -403,4 +403,6 @@
public static native int getPrimaryOutputFrameCount();
public static native int getOutputLatency(int stream);
+ public static native int setLowRamDevice(boolean isLowRamDevice);
+
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index e60ecc4..4a1646b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -112,10 +112,10 @@
oneway void setRemoteSubmixOn(boolean on, int address);
- int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher l,
- String clientId, String callingPackageName);
+ int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb,
+ IAudioFocusDispatcher fd, String clientId, String callingPackageName);
- int abandonAudioFocus(IAudioFocusDispatcher l, String clientId);
+ int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId);
void unregisterAudioFocusClient(String clientId);
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
new file mode 100644
index 0000000..37c9e4a
--- /dev/null
+++ b/media/java/android/media/MediaFocusControl.java
@@ -0,0 +1,2446 @@
+/*
+ * 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.media;
+
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.app.PendingIntent.OnFinished;
+import android.content.ActivityNotFoundException;
+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.pm.PackageManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.IBinder.DeathRecipient;
+import android.provider.Settings;
+import android.speech.RecognizerIntent;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Stack;
+
+/**
+ * @hide
+ *
+ */
+public class MediaFocusControl implements OnFinished {
+
+ private static final String TAG = "MediaFocusControl";
+
+ /** Debug remote control client/display feature */
+ protected static final boolean DEBUG_RC = false;
+ /** Debug volumes */
+ protected static final boolean DEBUG_VOL = false;
+
+ /** Used to alter media button redirection when the phone is ringing. */
+ private boolean mIsRinging = false;
+
+ private final PowerManager.WakeLock mMediaEventWakeLock;
+ private final MediaEventHandler mEventHandler;
+ private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private final VolumeController mVolumeController;
+ private final BroadcastReceiver mReceiver = new PackageIntentsReceiver();
+ private final AppOpsManager mAppOps;
+ private final KeyguardManager mKeyguardManager;
+ private final AudioService mAudioService;
+
+ protected MediaFocusControl(Looper looper, Context cntxt,
+ VolumeController volumeCtrl, AudioService as) {
+ mEventHandler = new MediaEventHandler(looper);
+ mContext = cntxt;
+ mContentResolver = mContext.getContentResolver();
+ mVolumeController = volumeCtrl;
+ mAudioService = as;
+
+ PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
+ mMainRemote = new RemotePlaybackState(-1,
+ AudioService.getMaxStreamVolume(AudioManager.STREAM_MUSIC),
+ AudioService.getMaxStreamVolume(AudioManager.STREAM_MUSIC));
+
+ // Register for phone state monitoring
+ TelephonyManager tmgr = (TelephonyManager)
+ mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+
+ // Register for package addition/removal/change 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");
+ mContext.registerReceiver(mReceiver, pkgFilter);
+
+ mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mKeyguardManager =
+ (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+
+ mHasRemotePlayback = false;
+ mMainRemoteIsActive = false;
+ postReevaluateRemote();
+ }
+
+ // event handler messages
+ private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 0;
+ private static final int MSG_RCDISPLAY_CLEAR = 1;
+ private static final int MSG_RCDISPLAY_UPDATE = 2;
+ private static final int MSG_REEVALUATE_REMOTE = 3;
+ private static final int MSG_RCC_NEW_PLAYBACK_INFO = 4;
+ private static final int MSG_RCC_NEW_VOLUME_OBS = 5;
+ private static final int MSG_PROMOTE_RCC = 6;
+ private static final int MSG_RCC_NEW_PLAYBACK_STATE = 7;
+ private static final int MSG_RCC_SEEK_REQUEST = 8;
+
+ // sendMsg() flags
+ /** If the msg is already queued, replace it with this one. */
+ private static final int SENDMSG_REPLACE = 0;
+ /** If the msg is already queued, ignore this one and leave the old. */
+ private static final int SENDMSG_NOOP = 1;
+ /** If the msg is already queued, queue this one and leave the old. */
+ private static final int SENDMSG_QUEUE = 2;
+
+ private static void sendMsg(Handler handler, int msg,
+ int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
+
+ if (existingMsgPolicy == SENDMSG_REPLACE) {
+ handler.removeMessages(msg);
+ } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
+ return;
+ }
+
+ handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
+ }
+
+ private class MediaEventHandler extends Handler {
+ MediaEventHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_PERSIST_MEDIABUTTONRECEIVER:
+ onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
+ break;
+
+ case MSG_RCDISPLAY_CLEAR:
+ onRcDisplayClear();
+ break;
+
+ case MSG_RCDISPLAY_UPDATE:
+ // msg.obj is guaranteed to be non null
+ onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
+ break;
+
+ case MSG_REEVALUATE_REMOTE:
+ onReevaluateRemote();
+ break;
+
+ case MSG_RCC_NEW_PLAYBACK_INFO:
+ onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
+ ((Integer)msg.obj).intValue() /* value */);
+ break;
+ case MSG_RCC_NEW_VOLUME_OBS:
+ onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
+ (IRemoteVolumeObserver)msg.obj /* rvo */);
+ break;
+ case MSG_RCC_NEW_PLAYBACK_STATE:
+ onNewPlaybackStateForRcc(msg.arg1 /* rccId */,
+ msg.arg2 /* state */,
+ (RccPlaybackState)msg.obj /* newState */);
+ break;
+ case MSG_RCC_SEEK_REQUEST:
+ onSetRemoteControlClientPlaybackPosition(
+ msg.arg1 /* generationId */, ((Long)msg.obj).longValue() /* timeMs */);
+
+ case MSG_PROMOTE_RCC:
+ onPromoteRcc(msg.arg1);
+ break;
+ }
+ }
+ }
+
+ protected void dump(PrintWriter pw) {
+ dumpFocusStack(pw);
+ dumpRCStack(pw);
+ dumpRCCStack(pw);
+ dumpRCDList(pw);
+ }
+
+ private class PackageIntentsReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ 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) {
+ 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);
+ }
+ }
+ }
+ }
+
+ //==========================================================================================
+ // AudioFocus
+ //==========================================================================================
+
+ /* constant to identify focus stack entry that is used to hold the focus while the phone
+ * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
+ * entering and exiting calls.
+ */
+ protected final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
+
+ private final static Object mAudioFocusLock = new Object();
+
+ private final static Object mRingingLock = new Object();
+
+ private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (state == TelephonyManager.CALL_STATE_RINGING) {
+ //Log.v(TAG, " CALL_STATE_RINGING");
+ synchronized(mRingingLock) {
+ mIsRinging = true;
+ }
+ } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
+ || (state == TelephonyManager.CALL_STATE_IDLE)) {
+ synchronized(mRingingLock) {
+ mIsRinging = false;
+ }
+ }
+ }
+ };
+
+ /**
+ * Discard the current audio focus owner.
+ * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
+ * focus), remove it from the stack, and clear the remote control display.
+ */
+ protected void discardAudioFocusOwner() {
+ synchronized(mAudioFocusLock) {
+ if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+ // notify the current focus owner it lost focus after removing it from stack
+ FocusStackEntry focusOwner = mFocusStack.pop();
+ try {
+ focusOwner.mFocusDispatcher.dispatchAudioFocusChange(
+ AudioManager.AUDIOFOCUS_LOSS, focusOwner.mClientId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failure to signal loss of audio focus due to "+ e);
+ e.printStackTrace();
+ }
+ focusOwner.unlinkToDeath();
+ // clear RCD
+ synchronized(mRCStack) {
+ clearRemoteControlDisplay_syncAfRcs();
+ }
+ }
+ }
+ }
+
+ private void notifyTopOfAudioFocusStack() {
+ // notify the top of the stack it gained focus
+ if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+ if (canReassignAudioFocus()) {
+ try {
+ mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
+ AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private static class FocusStackEntry {
+ public int mStreamType = -1;// no stream type
+ public IAudioFocusDispatcher mFocusDispatcher = null;
+ public IBinder mSourceRef = null;
+ public String mClientId;
+ public int mFocusChangeType;
+ public AudioFocusDeathHandler mHandler;
+ public String mPackageName;
+ public int mCallingUid;
+
+ public FocusStackEntry() {
+ }
+
+ public FocusStackEntry(int streamType, int duration,
+ IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
+ String pn, int uid) {
+ mStreamType = streamType;
+ mFocusDispatcher = afl;
+ mSourceRef = source;
+ mClientId = id;
+ mFocusChangeType = duration;
+ mHandler = hdlr;
+ mPackageName = pn;
+ mCallingUid = uid;
+ }
+
+ public void unlinkToDeath() {
+ try {
+ if (mSourceRef != null && mHandler != null) {
+ mSourceRef.unlinkToDeath(mHandler, 0);
+ mHandler = null;
+ }
+ } catch (java.util.NoSuchElementException e) {
+ Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ unlinkToDeath(); // unlink exception handled inside method
+ super.finalize();
+ }
+ }
+
+ private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
+
+ /**
+ * Helper function:
+ * Display in the log the current entries in the audio focus stack
+ */
+ private void dumpFocusStack(PrintWriter pw) {
+ pw.println("\nAudio Focus stack entries (last is top of stack):");
+ synchronized(mAudioFocusLock) {
+ Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
+ while(stackIterator.hasNext()) {
+ FocusStackEntry fse = stackIterator.next();
+ pw.println(" source:" + fse.mSourceRef
+ + " -- pack: " + fse.mPackageName
+ + " -- client: " + fse.mClientId
+ + " -- duration: " + fse.mFocusChangeType
+ + " -- uid: " + fse.mCallingUid
+ + " -- stream: " + fse.mStreamType);
+ }
+ }
+ }
+
+ /**
+ * Helper function:
+ * Called synchronized on mAudioFocusLock
+ * Remove a focus listener from the focus stack.
+ * @param clientToRemove the focus listener
+ * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
+ * focus, notify the next item in the stack it gained focus.
+ */
+ private void removeFocusStackEntry(String clientToRemove, boolean signal) {
+ // is the current top of the focus stack abandoning focus? (because of request, not death)
+ if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
+ {
+ //Log.i(TAG, " removeFocusStackEntry() removing top of stack");
+ FocusStackEntry fse = mFocusStack.pop();
+ fse.unlinkToDeath();
+ if (signal) {
+ // notify the new top of the stack it gained focus
+ notifyTopOfAudioFocusStack();
+ // there's a new top of the stack, let the remote control know
+ synchronized(mRCStack) {
+ checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ }
+ }
+ } else {
+ // focus is abandoned by a client that's not at the top of the stack,
+ // no need to update focus.
+ // (using an iterator on the stack so we can safely remove an entry after having
+ // evaluated it, traversal order doesn't matter here)
+ Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
+ while(stackIterator.hasNext()) {
+ FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
+ if(fse.mClientId.equals(clientToRemove)) {
+ Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
+ + fse.mClientId);
+ stackIterator.remove();
+ fse.unlinkToDeath();
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper function:
+ * Called synchronized on mAudioFocusLock
+ * Remove focus listeners from the focus stack for a particular client when it has died.
+ */
+ private void removeFocusStackEntryForClient(IBinder cb) {
+ // is the owner of the audio focus part of the client to remove?
+ boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
+ mFocusStack.peek().mSourceRef.equals(cb);
+ // (using an iterator on the stack so we can safely remove an entry after having
+ // evaluated it, traversal order doesn't matter here)
+ Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
+ while(stackIterator.hasNext()) {
+ FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
+ if(fse.mSourceRef.equals(cb)) {
+ Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
+ + fse.mClientId);
+ stackIterator.remove();
+ // the client just died, no need to unlink to its death
+ }
+ }
+ if (isTopOfStackForClientToRemove) {
+ // we removed an entry at the top of the stack:
+ // notify the new top of the stack it gained focus.
+ notifyTopOfAudioFocusStack();
+ // there's a new top of the stack, let the remote control know
+ synchronized(mRCStack) {
+ checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ }
+ }
+ }
+
+ /**
+ * Helper function:
+ * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
+ */
+ private boolean canReassignAudioFocus() {
+ // focus requests are rejected during a phone call or when the phone is ringing
+ // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
+ if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Inner class to monitor audio focus client deaths, and remove them from the audio focus
+ * stack if necessary.
+ */
+ private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
+ private IBinder mCb; // To be notified of client's death
+
+ AudioFocusDeathHandler(IBinder cb) {
+ mCb = cb;
+ }
+
+ public void binderDied() {
+ synchronized(mAudioFocusLock) {
+ Log.w(TAG, " AudioFocus audio focus client died");
+ removeFocusStackEntryForClient(mCb);
+ }
+ }
+
+ public IBinder getBinder() {
+ return mCb;
+ }
+ }
+
+
+ /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int) */
+ protected int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
+ IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
+ Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
+ // the main stream type for the audio focus request is currently not used. It may
+ // potentially be used to handle multiple stream type-dependent audio focuses.
+
+ // we need a valid binder callback for clients
+ if (!cb.pingBinder()) {
+ Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+
+ if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
+ callingPackageName) != AppOpsManager.MODE_ALLOWED) {
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+
+ synchronized(mAudioFocusLock) {
+ if (!canReassignAudioFocus()) {
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+
+ // handle the potential premature death of the new holder of the focus
+ // (premature death == death before abandoning focus)
+ // Register for client death notification
+ AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
+ try {
+ cb.linkToDeath(afdh, 0);
+ } catch (RemoteException e) {
+ // client has already died!
+ Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+
+ if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
+ // if focus is already owned by this client and the reason for acquiring the focus
+ // hasn't changed, don't do anything
+ if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
+ // unlink death handler so it can be gc'ed.
+ // linkToDeath() creates a JNI global reference preventing collection.
+ cb.unlinkToDeath(afdh, 0);
+ return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ }
+ // the reason for the audio focus request has changed: remove the current top of
+ // stack and respond as if we had a new focus owner
+ FocusStackEntry fse = mFocusStack.pop();
+ fse.unlinkToDeath();
+ }
+
+ // notify current top of stack it is losing focus
+ if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+ try {
+ mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
+ -1 * focusChangeHint, // loss and gain codes are inverse of each other
+ mFocusStack.peek().mClientId);
+ } catch (RemoteException e) {
+ Log.e(TAG, " Failure to signal loss of focus due to "+ e);
+ e.printStackTrace();
+ }
+ }
+
+ // focus requester might already be somewhere below in the stack, remove it
+ removeFocusStackEntry(clientId, false /* signal */);
+
+ // push focus requester at the top of the audio focus stack
+ mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
+ clientId, afdh, callingPackageName, Binder.getCallingUid()));
+
+ // there's a new top of the stack, let the remote control know
+ synchronized(mRCStack) {
+ checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ }
+ }//synchronized(mAudioFocusLock)
+
+ return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ }
+
+ /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener) */
+ protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
+ Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
+ try {
+ // this will take care of notifying the new focus owner if needed
+ synchronized(mAudioFocusLock) {
+ removeFocusStackEntry(clientId, true);
+ }
+ } catch (java.util.ConcurrentModificationException cme) {
+ // Catching this exception here is temporary. It is here just to prevent
+ // a crash seen when the "Silent" notification is played. This is believed to be fixed
+ // but this try catch block is left just to be safe.
+ Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme);
+ cme.printStackTrace();
+ }
+
+ return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ }
+
+
+ protected void unregisterAudioFocusClient(String clientId) {
+ synchronized(mAudioFocusLock) {
+ removeFocusStackEntry(clientId, false);
+ }
+ }
+
+
+ //==========================================================================================
+ // RemoteControl
+ //==========================================================================================
+ protected void dispatchMediaKeyEvent(KeyEvent keyEvent) {
+ filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
+ }
+
+ protected void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
+ filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
+ }
+
+ private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+ // sanity check on the incoming key event
+ if (!isValidMediaKeyEvent(keyEvent)) {
+ Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
+ return;
+ }
+ // event filtering for telephony
+ synchronized(mRingingLock) {
+ synchronized(mRCStack) {
+ if ((mMediaReceiverForCalls != null) &&
+ (mIsRinging || (mAudioService.getMode() == AudioSystem.MODE_IN_CALL))) {
+ dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
+ return;
+ }
+ }
+ }
+ // event filtering based on voice-based interactions
+ if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
+ filterVoiceInputKeyEvent(keyEvent, needWakeLock);
+ } else {
+ dispatchMediaKeyEvent(keyEvent, needWakeLock);
+ }
+ }
+
+ /**
+ * Handles the dispatching of the media button events to the telephony package.
+ * Precondition: mMediaReceiverForCalls != null
+ * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
+ * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
+ * is dispatched.
+ */
+ private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
+ Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+ keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
+ if (needWakeLock) {
+ mMediaEventWakeLock.acquire();
+ keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone, mEventHandler, Activity.RESULT_OK, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Handles the dispatching of the media button events to one of the registered listeners,
+ * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
+ * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
+ * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
+ * is dispatched.
+ */
+ private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+ if (needWakeLock) {
+ mMediaEventWakeLock.acquire();
+ }
+ Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+ keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ synchronized(mRCStack) {
+ if (!mRCStack.empty()) {
+ // send the intent that was registered by the client
+ try {
+ mRCStack.peek().mMediaIntent.send(mContext,
+ needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
+ keyIntent, this, mEventHandler);
+ } catch (CanceledException e) {
+ Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
+ e.printStackTrace();
+ }
+ } else {
+ // legacy behavior when nobody registered their media button event receiver
+ // through AudioManager
+ if (needWakeLock) {
+ keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone,
+ mEventHandler, Activity.RESULT_OK, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+ }
+
+ /**
+ * The different actions performed in response to a voice button key event.
+ */
+ private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
+ private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
+ private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
+
+ private final Object mVoiceEventLock = new Object();
+ private boolean mVoiceButtonDown;
+ private boolean mVoiceButtonHandled;
+
+ /**
+ * Filter key events that may be used for voice-based interactions
+ * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
+ * media buttons that can be used to trigger voice-based interactions.
+ * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
+ * is dispatched.
+ */
+ private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+ if (DEBUG_RC) {
+ Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
+ }
+
+ int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
+ int keyAction = keyEvent.getAction();
+ synchronized (mVoiceEventLock) {
+ if (keyAction == KeyEvent.ACTION_DOWN) {
+ if (keyEvent.getRepeatCount() == 0) {
+ // initial down
+ mVoiceButtonDown = true;
+ mVoiceButtonHandled = false;
+ } else if (mVoiceButtonDown && !mVoiceButtonHandled
+ && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
+ // long-press, start voice-based interactions
+ mVoiceButtonHandled = true;
+ voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
+ }
+ } else if (keyAction == KeyEvent.ACTION_UP) {
+ if (mVoiceButtonDown) {
+ // voice button up
+ mVoiceButtonDown = false;
+ if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
+ voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
+ }
+ }
+ }
+ }//synchronized (mVoiceEventLock)
+
+ // take action after media button event filtering for voice-based interactions
+ switch (voiceButtonAction) {
+ case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
+ if (DEBUG_RC) Log.v(TAG, " ignore key event");
+ break;
+ case VOICEBUTTON_ACTION_START_VOICE_INPUT:
+ if (DEBUG_RC) Log.v(TAG, " start voice-based interactions");
+ // then start the voice-based interactions
+ startVoiceBasedInteractions(needWakeLock);
+ break;
+ case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
+ if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock);
+ sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
+ break;
+ }
+ }
+
+ private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
+ // send DOWN event
+ KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
+ dispatchMediaKeyEvent(keyEvent, needWakeLock);
+ // send UP event
+ keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
+ dispatchMediaKeyEvent(keyEvent, needWakeLock);
+
+ }
+
+
+ private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
+ if (keyEvent == null) {
+ return false;
+ }
+ final int keyCode = keyEvent.getKeyCode();
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MUTE:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_MEDIA_RECORD:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ case KeyEvent.KEYCODE_MEDIA_CLOSE:
+ case KeyEvent.KEYCODE_MEDIA_EJECT:
+ case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether the given key code is one that can trigger the launch of voice-based
+ * interactions.
+ * @param keyCode the key code associated with the key event
+ * @return true if the key is one of the supported voice-based interaction triggers
+ */
+ private static boolean isValidVoiceInputKeyCode(int keyCode) {
+ if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Tell the system to start voice-based interactions / voice commands
+ */
+ private void startVoiceBasedInteractions(boolean needWakeLock) {
+ Intent voiceIntent = null;
+ // select which type of search to launch:
+ // - screen on and device unlocked: action is ACTION_WEB_SEARCH
+ // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
+ // with EXTRA_SECURE set to true if the device is securely locked
+ PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+ if (!isLocked && pm.isScreenOn()) {
+ voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+ Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
+ } else {
+ voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
+ isLocked && mKeyguardManager.isKeyguardSecure());
+ Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
+ }
+ // start the search activity
+ if (needWakeLock) {
+ mMediaEventWakeLock.acquire();
+ }
+ try {
+ if (voiceIntent != null) {
+ voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ mContext.startActivity(voiceIntent);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "No activity for search: " + e);
+ } finally {
+ if (needWakeLock) {
+ mMediaEventWakeLock.release();
+ }
+ }
+ }
+
+ private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
+
+ // only set when wakelock was acquired, no need to check value when received
+ private static final String EXTRA_WAKELOCK_ACQUIRED =
+ "android.media.AudioService.WAKELOCK_ACQUIRED";
+
+ public void onSendFinished(PendingIntent pendingIntent, Intent intent,
+ int resultCode, String resultData, Bundle resultExtras) {
+ if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
+ mMediaEventWakeLock.release();
+ }
+ }
+
+ BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ Bundle extras = intent.getExtras();
+ if (extras == null) {
+ return;
+ }
+ if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
+ mMediaEventWakeLock.release();
+ }
+ }
+ };
+
+ /**
+ * 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.
+ * This object may be null.
+ * Access protected by mCurrentRcLock.
+ */
+ private IRemoteControlClient mCurrentRcClient = null;
+
+ private final static int RC_INFO_NONE = 0;
+ private final static int RC_INFO_ALL =
+ RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
+ RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
+ RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
+ RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
+
+ /**
+ * A monotonically increasing generation counter for mCurrentRcClient.
+ * Only accessed with a lock on mCurrentRcLock.
+ * No value wrap-around issues as we only act on equal values.
+ */
+ private int mCurrentRcClientGen = 0;
+
+ /**
+ * Inner class to monitor remote control client deaths, and remove the client for the
+ * remote control stack if necessary.
+ */
+ private class RcClientDeathHandler implements IBinder.DeathRecipient {
+ final private IBinder mCb; // To be notified of client's death
+ final private PendingIntent mMediaIntent;
+
+ RcClientDeathHandler(IBinder cb, PendingIntent pi) {
+ mCb = cb;
+ mMediaIntent = pi;
+ }
+
+ public void binderDied() {
+ Log.w(TAG, " RemoteControlClient died");
+ // remote control client died, make sure the displays don't use it anymore
+ // by setting its remote control client to null
+ registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
+ // the dead client was maybe handling remote playback, reevaluate
+ postReevaluateRemote();
+ }
+
+ public IBinder getBinder() {
+ return mCb;
+ }
+ }
+
+ /**
+ * A global counter for RemoteControlClient identifiers
+ */
+ private static int sLastRccId = 0;
+
+ private class RemotePlaybackState {
+ int mRccId;
+ int mVolume;
+ int mVolumeMax;
+ int mVolumeHandling;
+
+ private RemotePlaybackState(int id, int vol, int volMax) {
+ mRccId = id;
+ mVolume = vol;
+ mVolumeMax = volMax;
+ mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
+ }
+ }
+
+ /**
+ * Internal cache for the playback information of the RemoteControlClient whose volume gets to
+ * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
+ * every time we need this info.
+ */
+ private RemotePlaybackState mMainRemote;
+ /**
+ * Indicates whether the "main" RemoteControlClient is considered active.
+ * Use synchronized on mMainRemote.
+ */
+ private boolean mMainRemoteIsActive;
+ /**
+ * Indicates whether there is remote playback going on. True even if there is no "active"
+ * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
+ * handles remote playback.
+ * Use synchronized on mMainRemote.
+ */
+ private boolean mHasRemotePlayback;
+
+ private static class RccPlaybackState {
+ public int mState;
+ public long mPositionMs;
+ public float mSpeed;
+
+ public RccPlaybackState(int state, long positionMs, float speed) {
+ mState = state;
+ mPositionMs = positionMs;
+ mSpeed = speed;
+ }
+
+ public void reset() {
+ mState = RemoteControlClient.PLAYSTATE_STOPPED;
+ mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
+ mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
+ }
+
+ @Override
+ public String toString() {
+ return stateToString() + ", "
+ + ((mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) ?
+ "PLAYBACK_POSITION_INVALID ," : String.valueOf(mPositionMs)) + "ms ,"
+ + mSpeed + "X";
+ }
+
+ private String stateToString() {
+ switch (mState) {
+ case RemoteControlClient.PLAYSTATE_NONE:
+ return "PLAYSTATE_NONE";
+ case RemoteControlClient.PLAYSTATE_STOPPED:
+ return "PLAYSTATE_STOPPED";
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ return "PLAYSTATE_PAUSED";
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ return "PLAYSTATE_PLAYING";
+ case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+ return "PLAYSTATE_FAST_FORWARDING";
+ case RemoteControlClient.PLAYSTATE_REWINDING:
+ return "PLAYSTATE_REWINDING";
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ return "PLAYSTATE_SKIPPING_FORWARDS";
+ case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+ return "PLAYSTATE_SKIPPING_BACKWARDS";
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ return "PLAYSTATE_BUFFERING";
+ case RemoteControlClient.PLAYSTATE_ERROR:
+ return "PLAYSTATE_ERROR";
+ default:
+ return "[invalid playstate]";
+ }
+ }
+ }
+
+ protected static class RemoteControlStackEntry implements DeathRecipient {
+ public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+ final public MediaFocusControl mController;
+ /**
+ * The target for the ACTION_MEDIA_BUTTON events.
+ * Always non null.
+ */
+ final public PendingIntent mMediaIntent;
+ /**
+ * The registered media button event receiver.
+ * Always non null.
+ */
+ final public ComponentName mReceiverComponent;
+ public IBinder mToken;
+ public String mCallingPackageName;
+ public int mCallingUid;
+ /**
+ * Provides access to the information to display on the remote control.
+ * May be null (when a media button event receiver is registered,
+ * but no remote control client has been registered) */
+ public IRemoteControlClient mRcClient;
+ public RcClientDeathHandler mRcClientDeathHandler;
+ /**
+ * Information only used for non-local playback
+ */
+ public int mPlaybackType;
+ public int mPlaybackVolume;
+ public int mPlaybackVolumeMax;
+ public int mPlaybackVolumeHandling;
+ public int mPlaybackStream;
+ public RccPlaybackState mPlaybackState;
+ public IRemoteVolumeObserver mRemoteVolumeObs;
+
+ public void resetPlaybackInfo() {
+ mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
+ mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
+ mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
+ mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
+ mPlaybackStream = AudioManager.STREAM_MUSIC;
+ mPlaybackState.reset();
+ mRemoteVolumeObs = null;
+ }
+
+ /** precondition: mediaIntent != null */
+ public RemoteControlStackEntry(MediaFocusControl controller, PendingIntent mediaIntent,
+ ComponentName eventReceiver, IBinder token) {
+ mController = controller;
+ mMediaIntent = mediaIntent;
+ mReceiverComponent = eventReceiver;
+ mToken = token;
+ mCallingUid = -1;
+ mRcClient = null;
+ mRccId = ++sLastRccId;
+ mPlaybackState = new RccPlaybackState(
+ RemoteControlClient.PLAYSTATE_STOPPED,
+ RemoteControlClient.PLAYBACK_POSITION_INVALID,
+ RemoteControlClient.PLAYBACK_SPEED_1X);
+
+ resetPlaybackInfo();
+ if (mToken != null) {
+ try {
+ mToken.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ mController.mEventHandler.post(new Runnable() {
+ @Override public void run() {
+ mController.unregisterMediaButtonIntent(mMediaIntent);
+ }
+ });
+ }
+ }
+ }
+
+ public void unlinkToRcClientDeath() {
+ if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
+ try {
+ mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
+ mRcClientDeathHandler = null;
+ } catch (java.util.NoSuchElementException e) {
+ // not much we can do here
+ Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void destroy() {
+ unlinkToRcClientDeath();
+ if (mToken != null) {
+ mToken.unlinkToDeath(this, 0);
+ mToken = null;
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mController.unregisterMediaButtonIntent(mMediaIntent);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ destroy(); // unlink exception handled inside method
+ super.finalize();
+ }
+ }
+
+ /**
+ * The stack of remote control event receivers.
+ * Code sections and methods that modify the remote control event receiver stack are
+ * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
+ * stack, audio focus or RC, can lead to a change in the remote control display
+ */
+ private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
+
+ /**
+ * The component the telephony package can register so telephony calls have priority to
+ * handle media button events
+ */
+ private ComponentName mMediaReceiverForCalls = null;
+
+ /**
+ * Helper function:
+ * Display in the log the current entries in the remote control focus stack
+ */
+ private void dumpRCStack(PrintWriter pw) {
+ pw.println("\nRemote Control stack entries (last is top of stack):");
+ synchronized(mRCStack) {
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = stackIterator.next();
+ pw.println(" pi: " + rcse.mMediaIntent +
+ " -- pack: " + rcse.mCallingPackageName +
+ " -- ercvr: " + rcse.mReceiverComponent +
+ " -- client: " + rcse.mRcClient +
+ " -- uid: " + rcse.mCallingUid +
+ " -- type: " + rcse.mPlaybackType +
+ " state: " + rcse.mPlaybackState);
+ }
+ }
+ }
+
+ /**
+ * Helper function:
+ * Display in the log the current entries in the remote control stack, focusing
+ * on RemoteControlClient data
+ */
+ private void dumpRCCStack(PrintWriter pw) {
+ pw.println("\nRemote Control Client stack entries (last is top of stack):");
+ synchronized(mRCStack) {
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = stackIterator.next();
+ pw.println(" uid: " + rcse.mCallingUid +
+ " -- id: " + rcse.mRccId +
+ " -- type: " + rcse.mPlaybackType +
+ " -- state: " + rcse.mPlaybackState +
+ " -- vol handling: " + rcse.mPlaybackVolumeHandling +
+ " -- vol: " + rcse.mPlaybackVolume +
+ " -- volMax: " + rcse.mPlaybackVolumeMax +
+ " -- volObs: " + rcse.mRemoteVolumeObs);
+ }
+ synchronized(mCurrentRcLock) {
+ pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
+ }
+ }
+ synchronized (mMainRemote) {
+ pw.println("\nRemote Volume State:");
+ pw.println(" has remote: " + mHasRemotePlayback);
+ pw.println(" is remote active: " + mMainRemoteIsActive);
+ pw.println(" rccId: " + mMainRemote.mRccId);
+ pw.println(" volume handling: "
+ + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
+ "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
+ pw.println(" volume: " + mMainRemote.mVolume);
+ pw.println(" volume steps: " + mMainRemote.mVolumeMax);
+ }
+ }
+
+ /**
+ * Helper function:
+ * Display in the log the current entries in the list of remote control displays
+ */
+ private void dumpRCDList(PrintWriter pw) {
+ pw.println("\nRemote Control Display list entries:");
+ synchronized(mRCStack) {
+ final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+ while (displayIterator.hasNext()) {
+ final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+ pw.println(" IRCD: " + di.mRcDisplay +
+ " -- w:" + di.mArtworkExpectedWidth +
+ " -- h:" + di.mArtworkExpectedHeight+
+ " -- wantsPosSync:" + di.mWantsPositionSync);
+ }
+ }
+ }
+
+ /**
+ * Helper function:
+ * Remove any entry in the remote control stack that has the same package name as packageName
+ * Pre-condition: packageName != null
+ */
+ 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
+ // (using an iterator on the stack so we can safely remove an entry after having
+ // evaluated it, traversal order doesn't matter here)
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
+ if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
+ // a stack entry is from the package being removed, remove it from the stack
+ stackIterator.remove();
+ 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()) {
+ // no saved media button receiver
+ mEventHandler.sendMessage(
+ mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+ null));
+ } else if (oldTop != mRCStack.peek()) {
+ // the top of the stack has changed, save it in the system settings
+ // by posting a message to persist it; only do this however if it has
+ // a concrete component name (is not a transient registration)
+ RemoteControlStackEntry rcse = mRCStack.peek();
+ if (rcse.mReceiverComponent != null) {
+ mEventHandler.sendMessage(
+ mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+ rcse.mReceiverComponent));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper function:
+ * Restore remote control receiver from the system settings.
+ */
+ protected void restoreMediaButtonReceiver() {
+ String receiverName = Settings.System.getStringForUser(mContentResolver,
+ 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);
+ // the associated intent will be handled by the component being registered
+ mediaButtonIntent.setComponent(eventReceiver);
+ PendingIntent pi = PendingIntent.getBroadcast(mContext,
+ 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+ registerMediaButtonIntent(pi, eventReceiver, null);
+ }
+ }
+
+ /**
+ * 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
+ */
+ private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
+ IBinder token) {
+ // already at top of stack?
+ if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
+ return;
+ }
+ if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(),
+ mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+ RemoteControlStackEntry rcse = null;
+ boolean wasInsideStack = false;
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ rcse = mRCStack.elementAt(index);
+ if(rcse.mMediaIntent.equals(mediaIntent)) {
+ // ok to remove element while traversing the stack since we're leaving the loop
+ mRCStack.removeElementAt(index);
+ wasInsideStack = true;
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+ }
+ if (!wasInsideStack) {
+ rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
+ }
+ mRCStack.push(rcse); // rcse is never null
+
+ // post message to persist the default media button receiver
+ if (target != null) {
+ mEventHandler.sendMessage( mEventHandler.obtainMessage(
+ MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
+ }
+ }
+
+ /**
+ * Helper function:
+ * Remove the remote control receiver from the RC focus stack.
+ * Called synchronized on mAudioFocusLock, then mRCStack
+ * precondition: pi != null
+ */
+ private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) {
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if (rcse.mMediaIntent.equals(pi)) {
+ rcse.destroy();
+ // ok to remove element while traversing the stack since we're leaving the loop
+ mRCStack.removeElementAt(index);
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+ }
+ }
+
+ /**
+ * Helper function:
+ * Called synchronized on mRCStack
+ */
+ private boolean isCurrentRcController(PendingIntent pi) {
+ if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
+ return true;
+ }
+ return false;
+ }
+
+ private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
+ Settings.System.putStringForUser(mContentResolver,
+ Settings.System.MEDIA_BUTTON_RECEIVER,
+ receiver == null ? "" : receiver.flattenToString(),
+ UserHandle.USER_CURRENT);
+ }
+
+ //==========================================================================================
+ // Remote control display / client
+ //==========================================================================================
+ /**
+ * Update the remote control displays with the new "focused" client generation
+ */
+ private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
+ PendingIntent newMediaIntent, boolean clearing) {
+ synchronized(mRCStack) {
+ if (mRcDisplays.size() > 0) {
+ final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+ while (displayIterator.hasNext()) {
+ final DisplayInfoForServer di = displayIterator.next();
+ try {
+ di.mRcDisplay.setCurrentClientId(
+ newClientGeneration, newMediaIntent, clearing);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
+ di.release();
+ displayIterator.remove();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Update the remote control clients with the new "focused" client generation
+ */
+ private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
+ // (using an iterator on the stack so we can safely remove an entry if needed,
+ // traversal order doesn't matter here as we update all entries)
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry se = stackIterator.next();
+ if ((se != null) && (se.mRcClient != null)) {
+ try {
+ se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
+ stackIterator.remove();
+ se.unlinkToRcClientDeath();
+ }
+ }
+ }
+ }
+
+ /**
+ * Update the displays and clients with the new "focused" client generation and name
+ * @param newClientGeneration the new generation value matching a client update
+ * @param newMediaIntent the media button event receiver associated with the client.
+ * May be null, which implies there is no registered media button event receiver.
+ * @param clearing true if the new client generation value maps to a remote control update
+ * where the display should be cleared.
+ */
+ private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
+ PendingIntent newMediaIntent, boolean clearing) {
+ // send the new valid client generation ID to all displays
+ setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
+ // send the new valid client generation ID to all clients
+ setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
+ }
+
+ /**
+ * Called when processing MSG_RCDISPLAY_CLEAR event
+ */
+ private void onRcDisplayClear() {
+ if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
+
+ synchronized(mRCStack) {
+ synchronized(mCurrentRcLock) {
+ mCurrentRcClientGen++;
+ // synchronously update the displays and clients with the new client generation
+ setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
+ null /*newMediaIntent*/, true /*clearing*/);
+ }
+ }
+ }
+
+ /**
+ * Called when processing MSG_RCDISPLAY_UPDATE event
+ */
+ private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
+ synchronized(mRCStack) {
+ synchronized(mCurrentRcLock) {
+ if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
+ if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
+
+ mCurrentRcClientGen++;
+ // synchronously update the displays and clients with
+ // the new client generation
+ setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
+ rcse.mMediaIntent /*newMediaIntent*/,
+ false /*clearing*/);
+
+ // tell the current client that it needs to send info
+ try {
+ mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Current valid remote client is dead: "+e);
+ mCurrentRcClient = null;
+ }
+ } else {
+ // the remote control display owner has changed between the
+ // the message to update the display was sent, and the time it
+ // gets to be processed (now)
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Helper function:
+ * Called synchronized on mRCStack
+ */
+ private void clearRemoteControlDisplay_syncAfRcs() {
+ synchronized(mCurrentRcLock) {
+ mCurrentRcClient = null;
+ }
+ // will cause onRcDisplayClear() to be called in AudioService's handler thread
+ mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
+ }
+
+ /**
+ * Helper function for code readability: only to be called from
+ * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
+ * this method.
+ * Preconditions:
+ * - called synchronized mAudioFocusLock then on mRCStack
+ * - mRCStack.isEmpty() is false
+ */
+ private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
+ RemoteControlStackEntry rcse = mRCStack.peek();
+ int infoFlagsAboutToBeUsed = infoChangedFlags;
+ // this is where we enforce opt-in for information display on the remote controls
+ // with the new AudioManager.registerRemoteControlClient() API
+ if (rcse.mRcClient == null) {
+ //Log.w(TAG, "Can't update remote control display with null remote control client");
+ clearRemoteControlDisplay_syncAfRcs();
+ return;
+ }
+ synchronized(mCurrentRcLock) {
+ if (!rcse.mRcClient.equals(mCurrentRcClient)) {
+ // new RC client, assume every type of information shall be queried
+ infoFlagsAboutToBeUsed = RC_INFO_ALL;
+ }
+ mCurrentRcClient = rcse.mRcClient;
+ }
+ // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
+ mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
+ infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
+ }
+
+ /**
+ * Helper function:
+ * Called synchronized on mAudioFocusLock, then mRCStack
+ * Check whether the remote control display should be updated, triggers the update if required
+ * @param infoChangedFlags the flags corresponding to the remote control client information
+ * that has changed, if applicable (checking for the update conditions might trigger a
+ * clear, rather than an update event).
+ */
+ private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
+ // determine whether the remote control display should be refreshed
+ // if either stack is empty, there is a mismatch, so clear the RC display
+ if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
+ clearRemoteControlDisplay_syncAfRcs();
+ return;
+ }
+
+ // determine which entry in the AudioFocus stack to consider, and compare against the
+ // top of the stack for the media button event receivers : simply using the top of the
+ // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
+ // notifications playing during music playback.
+ // Crawl the AudioFocus stack from the top until an entry is found with the following
+ // characteristics:
+ // - focus gain on STREAM_MUSIC stream
+ // - non-transient focus gain on a stream other than music
+ FocusStackEntry af = null;
+ try {
+ for (int index = mFocusStack.size()-1; index >= 0; index--) {
+ FocusStackEntry fse = mFocusStack.elementAt(index);
+ if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
+ || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
+ af = fse;
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
+ af = null;
+ }
+ if (af == null) {
+ clearRemoteControlDisplay_syncAfRcs();
+ return;
+ }
+
+ // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
+ if ((mRCStack.peek().mCallingPackageName != null)
+ && (af.mPackageName != null)
+ && !(mRCStack.peek().mCallingPackageName.compareTo(
+ af.mPackageName) == 0)) {
+ clearRemoteControlDisplay_syncAfRcs();
+ return;
+ }
+ // if the audio focus didn't originate from the same Uid as the one in which the remote
+ // control information will be retrieved, clear
+ if (mRCStack.peek().mCallingUid != af.mCallingUid) {
+ clearRemoteControlDisplay_syncAfRcs();
+ return;
+ }
+
+ // refresh conditions were verified: update the remote controls
+ // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
+ updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
+ }
+
+ /**
+ * Helper function:
+ * Post a message to asynchronously move the media button event receiver associated with the
+ * given remote control client ID to the top of the remote control stack
+ * @param rccId
+ */
+ private void postPromoteRcc(int rccId) {
+ sendMsg(mEventHandler, MSG_PROMOTE_RCC, SENDMSG_REPLACE,
+ rccId /*arg1*/, 0, null, 0/*delay*/);
+ }
+
+ private void onPromoteRcc(int rccId) {
+ if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); }
+ synchronized(mAudioFocusLock) {
+ synchronized(mRCStack) {
+ // ignore if given RCC ID is already at top of remote control stack
+ if (!mRCStack.isEmpty() && (mRCStack.peek().mRccId == rccId)) {
+ return;
+ }
+ int indexToPromote = -1;
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if (rcse.mRccId == rccId) {
+ indexToPromote = index;
+ break;
+ }
+ }
+ if (indexToPromote >= 0) {
+ if (DEBUG_RC) { Log.d(TAG, " moving RCC from index " + indexToPromote
+ + " to " + (mRCStack.size()-1)); }
+ final RemoteControlStackEntry rcse = mRCStack.remove(indexToPromote);
+ mRCStack.push(rcse);
+ // the RC stack changed, reevaluate the display
+ checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+ }
+ }//synchronized(mRCStack)
+ }//synchronized(mAudioFocusLock)
+ }
+
+ /**
+ * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
+ * precondition: mediaIntent != null
+ */
+ protected void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
+ IBinder token) {
+ Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
+
+ synchronized(mAudioFocusLock) {
+ synchronized(mRCStack) {
+ pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token);
+ // new RC client, assume every type of information shall be queried
+ checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ }
+ }
+ }
+
+ /**
+ * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
+ * precondition: mediaIntent != null, eventReceiver != null
+ */
+ protected void unregisterMediaButtonIntent(PendingIntent mediaIntent)
+ {
+ Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent);
+
+ synchronized(mAudioFocusLock) {
+ synchronized(mRCStack) {
+ boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
+ removeMediaButtonReceiver_syncAfRcs(mediaIntent);
+ if (topOfStackWillChange) {
+ // current RC client will change, assume every type of info needs to be queried
+ checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ }
+ }
+ }
+ }
+
+ /**
+ * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
+ * precondition: c != null
+ */
+ protected void registerMediaButtonEventReceiverForCalls(ComponentName c) {
+ if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Invalid permissions to register media button receiver for calls");
+ return;
+ }
+ synchronized(mRCStack) {
+ mMediaReceiverForCalls = c;
+ }
+ }
+
+ /**
+ * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
+ */
+ protected void unregisterMediaButtonEventReceiverForCalls() {
+ if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
+ return;
+ }
+ synchronized(mRCStack) {
+ mMediaReceiverForCalls = null;
+ }
+ }
+
+ /**
+ * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
+ * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
+ * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
+ * without modifying the RC stack, but while still causing the display to refresh (will
+ * become blank as a result of this)
+ */
+ protected int registerRemoteControlClient(PendingIntent mediaIntent,
+ IRemoteControlClient rcClient, String callingPackageName) {
+ if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
+ int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+ synchronized(mAudioFocusLock) {
+ synchronized(mRCStack) {
+ // store the new display information
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if(rcse.mMediaIntent.equals(mediaIntent)) {
+ // already had a remote control client?
+ if (rcse.mRcClientDeathHandler != null) {
+ // stop monitoring the old client's death
+ rcse.unlinkToRcClientDeath();
+ }
+ // save the new remote control client
+ rcse.mRcClient = rcClient;
+ rcse.mCallingPackageName = callingPackageName;
+ rcse.mCallingUid = Binder.getCallingUid();
+ if (rcClient == null) {
+ // here rcse.mRcClientDeathHandler is null;
+ rcse.resetPlaybackInfo();
+ break;
+ }
+ rccId = rcse.mRccId;
+
+ // there is a new (non-null) client:
+ // 1/ give the new client the displays (if any)
+ if (mRcDisplays.size() > 0) {
+ plugRemoteControlDisplaysIntoClient_syncRcStack(rcse.mRcClient);
+ }
+ // 2/ monitor the new client's death
+ IBinder b = rcse.mRcClient.asBinder();
+ RcClientDeathHandler rcdh =
+ new RcClientDeathHandler(b, rcse.mMediaIntent);
+ try {
+ b.linkToDeath(rcdh, 0);
+ } catch (RemoteException e) {
+ // remote control client is DOA, disqualify it
+ Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
+ rcse.mRcClient = null;
+ }
+ rcse.mRcClientDeathHandler = rcdh;
+ break;
+ }
+ }//for
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+ }
+
+ // if the eventReceiver is at the top of the stack
+ // then check for potential refresh of the remote controls
+ if (isCurrentRcController(mediaIntent)) {
+ checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ }
+ }//synchronized(mRCStack)
+ }//synchronized(mAudioFocusLock)
+ return rccId;
+ }
+
+ /**
+ * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
+ * rcClient is guaranteed non-null
+ */
+ protected void unregisterRemoteControlClient(PendingIntent mediaIntent,
+ IRemoteControlClient rcClient) {
+ if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
+ synchronized(mAudioFocusLock) {
+ synchronized(mRCStack) {
+ boolean topRccChange = false;
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if ((rcse.mMediaIntent.equals(mediaIntent))
+ && rcClient.equals(rcse.mRcClient)) {
+ // we found the IRemoteControlClient to unregister
+ // stop monitoring its death
+ rcse.unlinkToRcClientDeath();
+ // reset the client-related fields
+ rcse.mRcClient = null;
+ rcse.mCallingPackageName = null;
+ topRccChange = (index == mRCStack.size()-1);
+ // there can only be one matching RCC in the RC stack, we're done
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+ }
+ if (topRccChange) {
+ // no more RCC for the RCD, check for potential refresh of the remote controls
+ checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * A class to encapsulate all the information about a remote control display.
+ * After instanciation, init() must always be called before the object is added in the list
+ * of displays.
+ * Before being removed from the list of displays, release() must always be called (otherwise
+ * it will leak death handlers).
+ */
+ private class DisplayInfoForServer implements IBinder.DeathRecipient {
+ /** may never be null */
+ private IRemoteControlDisplay mRcDisplay;
+ private IBinder mRcDisplayBinder;
+ private int mArtworkExpectedWidth = -1;
+ private int mArtworkExpectedHeight = -1;
+ private boolean mWantsPositionSync = false;
+
+ public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
+ if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
+ mRcDisplay = rcd;
+ mRcDisplayBinder = rcd.asBinder();
+ mArtworkExpectedWidth = w;
+ mArtworkExpectedHeight = h;
+ }
+
+ public boolean init() {
+ try {
+ mRcDisplayBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ // remote control display is DOA, disqualify it
+ Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
+ return false;
+ }
+ return true;
+ }
+
+ public void release() {
+ try {
+ mRcDisplayBinder.unlinkToDeath(this, 0);
+ } catch (java.util.NoSuchElementException e) {
+ // not much we can do here, the display should have been unregistered anyway
+ Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
+ }
+ }
+
+ public void binderDied() {
+ synchronized(mRCStack) {
+ Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
+ // remove the display from the list
+ final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+ while (displayIterator.hasNext()) {
+ final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+ if (di.mRcDisplay == mRcDisplay) {
+ if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
+ displayIterator.remove();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * The remote control displays.
+ * Access synchronized on mRCStack
+ */
+ private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
+
+ /**
+ * Plug each registered display into the specified client
+ * @param rcc, guaranteed non null
+ */
+ private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) {
+ final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+ while (displayIterator.hasNext()) {
+ final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+ try {
+ rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
+ di.mArtworkExpectedHeight);
+ if (di.mWantsPositionSync) {
+ rcc.setWantsSyncForDisplay(di.mRcDisplay, true);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
+ }
+ }
+ }
+
+ /**
+ * Is the remote control display interface already registered
+ * @param rcd
+ * @return true if the IRemoteControlDisplay is already in the list of displays
+ */
+ private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
+ final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+ while (displayIterator.hasNext()) {
+ final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+ if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Register an IRemoteControlDisplay.
+ * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
+ * at the top of the stack to update the new display with its information.
+ * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
+ * @param rcd the IRemoteControlDisplay to register. No effect if null.
+ * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
+ * display doesn't need to receive artwork.
+ * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
+ * display doesn't need to receive artwork.
+ */
+ protected void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
+ if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
+ synchronized(mAudioFocusLock) {
+ synchronized(mRCStack) {
+ if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
+ return;
+ }
+ DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
+ if (!di.init()) {
+ if (DEBUG_RC) Log.e(TAG, " error registering RCD");
+ return;
+ }
+ // add RCD to list of displays
+ mRcDisplays.add(di);
+
+ // let all the remote control clients know there is a new display (so the remote
+ // control stack traversal order doesn't matter).
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = stackIterator.next();
+ if(rcse.mRcClient != null) {
+ try {
+ rcse.mRcClient.plugRemoteControlDisplay(rcd, w, h);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error connecting RCD to client: ", e);
+ }
+ }
+ }
+
+ // we have a new display, of which all the clients are now aware: have it be updated
+ checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+ }
+ }
+ }
+
+ /**
+ * Unregister an IRemoteControlDisplay.
+ * No effect if the IRemoteControlDisplay hasn't been successfully registered.
+ * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
+ * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
+ */
+ protected void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
+ if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
+ synchronized(mRCStack) {
+ if (rcd == null) {
+ return;
+ }
+
+ boolean displayWasPluggedIn = false;
+ final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+ while (displayIterator.hasNext() && !displayWasPluggedIn) {
+ final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+ if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+ displayWasPluggedIn = true;
+ di.release();
+ displayIterator.remove();
+ }
+ }
+
+ if (displayWasPluggedIn) {
+ // disconnect this remote control display from all the clients, so the remote
+ // control stack traversal order doesn't matter
+ final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ final RemoteControlStackEntry rcse = stackIterator.next();
+ if(rcse.mRcClient != null) {
+ try {
+ rcse.mRcClient.unplugRemoteControlDisplay(rcd);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error disconnecting remote control display to client: ", e);
+ }
+ }
+ }
+ } else {
+ if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD");
+ }
+ }
+ }
+
+ /**
+ * Update the size of the artwork used by an IRemoteControlDisplay.
+ * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
+ * @param rcd the IRemoteControlDisplay with the new artwork size requirement
+ * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
+ * display doesn't need to receive artwork.
+ * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
+ * display doesn't need to receive artwork.
+ */
+ protected void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
+ synchronized(mRCStack) {
+ final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+ boolean artworkSizeUpdate = false;
+ while (displayIterator.hasNext() && !artworkSizeUpdate) {
+ final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+ if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+ if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
+ di.mArtworkExpectedWidth = w;
+ di.mArtworkExpectedHeight = h;
+ artworkSizeUpdate = true;
+ }
+ }
+ }
+ if (artworkSizeUpdate) {
+ // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
+ // stack traversal order doesn't matter
+ final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ final RemoteControlStackEntry rcse = stackIterator.next();
+ if(rcse.mRcClient != null) {
+ try {
+ rcse.mRcClient.setBitmapSizeForDisplay(rcd, w, h);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Controls whether a remote control display needs periodic checks of the RemoteControlClient
+ * playback position to verify that the estimated position has not drifted from the actual
+ * position. By default the check is not performed.
+ * The IRemoteControlDisplay must have been previously registered for this to have any effect.
+ * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
+ * or disabled. Not null.
+ * @param wantsSync if true, RemoteControlClient instances which expose their playback position
+ * to the framework will regularly compare the estimated playback position with the actual
+ * position, and will update the IRemoteControlDisplay implementation whenever a drift is
+ * detected.
+ */
+ protected void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
+ boolean wantsSync) {
+ synchronized(mRCStack) {
+ boolean rcdRegistered = false;
+ // store the information about this display
+ // (display stack traversal order doesn't matter).
+ final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+ while (displayIterator.hasNext()) {
+ final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+ if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+ di.mWantsPositionSync = wantsSync;
+ rcdRegistered = true;
+ break;
+ }
+ }
+ if (!rcdRegistered) {
+ return;
+ }
+ // notify all current RemoteControlClients
+ // (stack traversal order doesn't matter as we notify all RCCs)
+ final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while (stackIterator.hasNext()) {
+ final RemoteControlStackEntry rcse = stackIterator.next();
+ if (rcse.mRcClient != null) {
+ try {
+ rcse.mRcClient.setWantsSyncForDisplay(rcd, wantsSync);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e);
+ }
+ }
+ }
+ }
+ }
+
+ protected void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
+ // ignore position change requests if invalid generation ID
+ synchronized(mRCStack) {
+ synchronized(mCurrentRcLock) {
+ if (mCurrentRcClientGen != generationId) {
+ return;
+ }
+ }
+ }
+ // discard any unprocessed seek request in the message queue, and replace with latest
+ sendMsg(mEventHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
+ 0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
+ }
+
+ private 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;
+ }
+ }
+ }
+ }
+ }
+
+ protected void setPlaybackInfoForRcc(int rccId, int what, int value) {
+ sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
+ rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
+ }
+
+ // handler for MSG_RCC_NEW_PLAYBACK_INFO
+ private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
+ if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
+ ", what=" + key + ",val=" + value + ")");
+ synchronized(mRCStack) {
+ // iterating from top of stack as playback information changes are more likely
+ // on entries at the top of the remote control stack
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if (rcse.mRccId == rccId) {
+ switch (key) {
+ case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
+ rcse.mPlaybackType = value;
+ postReevaluateRemote();
+ break;
+ case RemoteControlClient.PLAYBACKINFO_VOLUME:
+ rcse.mPlaybackVolume = value;
+ synchronized (mMainRemote) {
+ if (rccId == mMainRemote.mRccId) {
+ mMainRemote.mVolume = value;
+ mVolumeController.postHasNewRemotePlaybackInfo();
+ }
+ }
+ break;
+ case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
+ rcse.mPlaybackVolumeMax = value;
+ synchronized (mMainRemote) {
+ if (rccId == mMainRemote.mRccId) {
+ mMainRemote.mVolumeMax = value;
+ mVolumeController.postHasNewRemotePlaybackInfo();
+ }
+ }
+ break;
+ case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
+ rcse.mPlaybackVolumeHandling = value;
+ synchronized (mMainRemote) {
+ if (rccId == mMainRemote.mRccId) {
+ mMainRemote.mVolumeHandling = value;
+ mVolumeController.postHasNewRemotePlaybackInfo();
+ }
+ }
+ break;
+ case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
+ rcse.mPlaybackStream = value;
+ break;
+ default:
+ Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
+ break;
+ }
+ return;
+ }
+ }//for
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e);
+ }
+ }
+ }
+
+ protected void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
+ sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
+ rccId /* arg1 */, state /* arg2 */,
+ new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
+ }
+
+ protected void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
+ if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
+ + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
+ synchronized(mRCStack) {
+ // iterating from top of stack as playback information changes are more likely
+ // on entries at the top of the remote control stack
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if (rcse.mRccId == rccId) {
+ rcse.mPlaybackState = newState;
+ synchronized (mMainRemote) {
+ if (rccId == mMainRemote.mRccId) {
+ mMainRemoteIsActive = isPlaystateActive(state);
+ postReevaluateRemote();
+ }
+ }
+ // an RCC moving to a "playing" state should become the media button
+ // event receiver so it can be controlled, without requiring the
+ // app to re-register its receiver
+ if (isPlaystateActive(state)) {
+ postPromoteRcc(rccId);
+ }
+ }
+ }//for
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e);
+ }
+ }
+ }
+
+ protected void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
+ sendMsg(mEventHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
+ rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
+ }
+
+ // handler for MSG_RCC_NEW_VOLUME_OBS
+ private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
+ synchronized(mRCStack) {
+ // The stack traversal order doesn't matter because there is only one stack entry
+ // with this RCC ID, but the matching ID is more likely at the top of the stack, so
+ // start iterating from the top.
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if (rcse.mRccId == rccId) {
+ rcse.mRemoteVolumeObs = rvo;
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+ }
+ }
+ }
+
+ /**
+ * Checks if a remote client is active on the supplied stream type. Update the remote stream
+ * volume state if found and playing
+ * @param streamType
+ * @return false if no remote playing is currently playing
+ */
+ protected boolean checkUpdateRemoteStateIfActive(int streamType) {
+ synchronized(mRCStack) {
+ // iterating from top of stack as active playback is more likely on entries at the top
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
+ && isPlaystateActive(rcse.mPlaybackState.mState)
+ && (rcse.mPlaybackStream == streamType)) {
+ if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
+ + ", vol =" + rcse.mPlaybackVolume);
+ synchronized (mMainRemote) {
+ mMainRemote.mRccId = rcse.mRccId;
+ mMainRemote.mVolume = rcse.mPlaybackVolume;
+ mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
+ mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
+ mMainRemoteIsActive = true;
+ }
+ return true;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+ }
+ }
+ synchronized (mMainRemote) {
+ mMainRemoteIsActive = false;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the given playback state is considered "active", i.e. it describes a state
+ * where playback is happening, or about to
+ * @param playState the playback state to evaluate
+ * @return true if active, false otherwise (inactive or unknown)
+ */
+ private static boolean isPlaystateActive(int playState) {
+ switch (playState) {
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+ case RemoteControlClient.PLAYSTATE_REWINDING:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected void adjustRemoteVolume(int streamType, int direction, int flags) {
+ int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+ boolean volFixed = false;
+ synchronized (mMainRemote) {
+ if (!mMainRemoteIsActive) {
+ if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
+ return;
+ }
+ rccId = mMainRemote.mRccId;
+ volFixed = (mMainRemote.mVolumeHandling ==
+ RemoteControlClient.PLAYBACK_VOLUME_FIXED);
+ }
+ // unlike "local" stream volumes, we can't compute the new volume based on the direction,
+ // we can only notify the remote that volume needs to be updated, and we'll get an async'
+ // update through setPlaybackInfoForRcc()
+ if (!volFixed) {
+ sendVolumeUpdateToRemote(rccId, direction);
+ }
+
+ // fire up the UI
+ mVolumeController.postRemoteVolumeChanged(streamType, flags);
+ }
+
+ private void sendVolumeUpdateToRemote(int rccId, int direction) {
+ if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
+ if (direction == 0) {
+ // only handling discrete events
+ return;
+ }
+ IRemoteVolumeObserver rvo = null;
+ synchronized (mRCStack) {
+ // The stack traversal order doesn't matter because there is only one stack entry
+ // with this RCC ID, but the matching ID is more likely at the top of the stack, so
+ // start iterating from the top.
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
+ if (rcse.mRccId == rccId) {
+ rvo = rcse.mRemoteVolumeObs;
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+ }
+ }
+ if (rvo != null) {
+ try {
+ rvo.dispatchRemoteVolumeUpdate(direction, -1);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error dispatching relative volume update", e);
+ }
+ }
+ }
+
+ protected int getRemoteStreamMaxVolume() {
+ synchronized (mMainRemote) {
+ if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
+ return 0;
+ }
+ return mMainRemote.mVolumeMax;
+ }
+ }
+
+ protected int getRemoteStreamVolume() {
+ synchronized (mMainRemote) {
+ if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
+ return 0;
+ }
+ return mMainRemote.mVolume;
+ }
+ }
+
+ protected void setRemoteStreamVolume(int vol) {
+ if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
+ int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+ synchronized (mMainRemote) {
+ if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
+ return;
+ }
+ rccId = mMainRemote.mRccId;
+ }
+ IRemoteVolumeObserver rvo = null;
+ synchronized (mRCStack) {
+ // The stack traversal order doesn't matter because there is only one stack entry
+ // with this RCC ID, but the matching ID is more likely at the top of the stack, so
+ // start iterating from the top.
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
+ if (rcse.mRccId == rccId) {
+ rvo = rcse.mRemoteVolumeObs;
+ break;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+ }
+ }
+ if (rvo != null) {
+ try {
+ rvo.dispatchRemoteVolumeUpdate(0, vol);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error dispatching absolute volume update", e);
+ }
+ }
+ }
+
+ /**
+ * Call to make AudioService reevaluate whether it's in a mode where remote players should
+ * have their volume controlled. In this implementation this is only to reset whether
+ * VolumePanel should display remote volumes
+ */
+ private void postReevaluateRemote() {
+ sendMsg(mEventHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
+ }
+
+ private void onReevaluateRemote() {
+ if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
+ // is there a registered RemoteControlClient that is handling remote playback
+ boolean hasRemotePlayback = false;
+ synchronized (mRCStack) {
+ // iteration stops when PLAYBACK_TYPE_REMOTE is found, so remote control stack
+ // traversal order doesn't matter
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = stackIterator.next();
+ if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
+ hasRemotePlayback = true;
+ break;
+ }
+ }
+ }
+ synchronized (mMainRemote) {
+ if (mHasRemotePlayback != hasRemotePlayback) {
+ mHasRemotePlayback = hasRemotePlayback;
+ mVolumeController.postRemoteSliderVisibility(hasRemotePlayback);
+ }
+ }
+ }
+
+}
diff --git a/media/java/android/media/VolumeController.java b/media/java/android/media/VolumeController.java
new file mode 100644
index 0000000..2d12bf2
--- /dev/null
+++ b/media/java/android/media/VolumeController.java
@@ -0,0 +1,29 @@
+/*
+ * 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.media;
+
+/**
+ * @hide
+ */
+public interface VolumeController {
+
+ public void postHasNewRemotePlaybackInfo();
+
+ public void postRemoteVolumeChanged(int streamType, int flags);
+
+ public void postRemoteSliderVisibility(boolean visible);
+}
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 5856057..d876bd2 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -542,7 +542,7 @@
nativeFormat = Image_getPixelFormat(env, format);
sp<BufferQueue> bq = new BufferQueue();
- sp<CpuConsumer> consumer = new CpuConsumer(bq, maxImages);
+ sp<CpuConsumer> consumer = new CpuConsumer(bq, true, maxImages);
// TODO: throw dvm exOutOfMemoryError?
if (consumer == NULL) {
jniThrowRuntimeException(env, "Failed to allocate native CpuConsumer");
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 0c2f3a3..131441b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -19,8 +19,10 @@
import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
import android.graphics.ImageFormat;
+import android.graphics.Rect;
import android.hardware.photography.CameraMetadata;
import android.hardware.photography.Rational;
+import android.hardware.photography.Size;
import static android.hardware.photography.CameraMetadata.*;
@@ -223,6 +225,10 @@
assertEquals(null, mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE));
+ // Write/read null values
+ mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, null);
+ assertEquals(null, mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE));
+
// Write 0 values
mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, new byte[] {});
@@ -286,13 +292,21 @@
}
private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T value) {
+ assertFalse("Use checkKeyGetAndSetArray to compare array Keys", type.isArray());
+
Key<T> key = new Key<T>(keyStr, type);
assertNull(mMetadata.get(key));
+ mMetadata.set(key, null);
+ assertNull(mMetadata.get(key));
mMetadata.set(key, value);
- assertEquals(value, mMetadata.get(key));
+
+ T actual = mMetadata.get(key);
+ assertEquals(value, actual);
}
private <T> void checkKeyGetAndSetArray(String keyStr, Class<T> type, T value) {
+ assertTrue(type.isArray());
+
Key<T> key = new Key<T>(keyStr, type);
assertNull(mMetadata.get(key));
mMetadata.set(key, value);
@@ -508,4 +522,73 @@
assertEquals(expectedIntValues[i], bf.getInt());
}
}
+
+ @SmallTest
+ public void testReadWriteSize() {
+ // int32 x n
+ checkKeyGetAndSet("android.jpeg.thumbnailSize", Size.class, new Size(123, 456));
+
+ // int32 x 2 x n
+ checkKeyGetAndSetArray("android.scaler.availableJpegSizes", Size[].class, new Size[] {
+ new Size(123, 456),
+ new Size(0xDEAD, 0xF00D),
+ new Size(0xF00, 0xB00)
+ });
+ }
+
+ @SmallTest
+ public void testReadWriteRectangle() {
+ // int32 x n
+ checkKeyGetAndSet("android.scaler.cropRegion", Rect.class, new Rect(10, 11, 1280, 1024));
+
+ // int32 x 2 x n
+ checkKeyGetAndSetArray("android.statistics.faceRectangles", Rect[].class, new Rect[] {
+ new Rect(110, 111, 11280, 11024),
+ new Rect(210, 111, 21280, 21024),
+ new Rect(310, 111, 31280, 31024)
+ });
+ }
+
+ @SmallTest
+ public void testReadWriteString() {
+ // (byte) string
+ Key<String> gpsProcessingMethodKey =
+ new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
+
+ String helloWorld = new String("HelloWorld");
+ byte[] helloWorldBytes = new byte[] {
+ 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0' };
+
+ mMetadata.set(gpsProcessingMethodKey, helloWorld);
+
+ String actual = mMetadata.get(gpsProcessingMethodKey);
+ assertEquals(helloWorld, actual);
+
+ byte[] actualBytes = mMetadata.readValues(getTag(gpsProcessingMethodKey.getName()));
+ assertArrayEquals(helloWorldBytes, actualBytes);
+
+ // Does not yet test as a string[] since we don't support that in native code.
+
+ // (byte) string
+ Key<String[]> gpsProcessingMethodKeyArray =
+ new Key<String[]>("android.jpeg.gpsProcessingMethod", String[].class);
+
+ String[] gpsStrings = new String[] { "HelloWorld", "FooBar", "Shazbot" };
+ byte[] gpsBytes = new byte[] {
+ 'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0',
+ 'F', 'o', 'o', 'B', 'a', 'r', '\0',
+ 'S', 'h', 'a', 'z', 'b', 'o', 't', '\0'};
+
+ mMetadata.set(gpsProcessingMethodKeyArray, gpsStrings);
+
+ String[] actualArray = mMetadata.get(gpsProcessingMethodKeyArray);
+ assertArrayEquals(gpsStrings, actualArray);
+
+ byte[] actualBytes2 = mMetadata.readValues(getTag(gpsProcessingMethodKeyArray.getName()));
+ assertArrayEquals(gpsBytes, actualBytes2);
+ }
+
+ <T> void compareGeneric(T expected, T actual) {
+ assertEquals(expected, actual);
+ }
}
diff --git a/packages/Keyguard/res/values-hdpi/dimens.xml b/packages/Keyguard/res/values-hdpi/dimens.xml
new file mode 100644
index 0000000..2c209e2
--- /dev/null
+++ b/packages/Keyguard/res/values-hdpi/dimens.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** 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.
+*/
+-->
+<resources>
+
+ <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) -->
+ <dimen name="keyguard_security_height">345dp</dimen>
+
+</resources>
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index a68fcdf..8ae0302 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -22,7 +22,7 @@
LOCAL_PACKAGE_NAME := PrintSpooler
-LOCAL_JAVA_LIBRARIES := framework
+LOCAL_JAVA_LIBRARIES := framework-base
LOCAL_CERTIFICATE := platform
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index fbb0060..f0bbfa4 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -46,7 +46,8 @@
<activity
android:name=".PrintJobConfigActivity"
- android:exported="true">
+ android:exported="true"
+ android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar">
</activity>
</application>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity.xml b/packages/PrintSpooler/res/layout/print_job_config_activity.xml
index 51e425d..8736bdd 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity.xml
@@ -14,248 +14,197 @@
limitations under the License.
-->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
- <GridLayout
- android:layout_width="wrap_content"
+ <ScrollView
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:orientation="vertical"
- android:columnCount="2">
+ android:background="@*android:color/bright_foreground_disabled_holo_light">
- <EditText
- android:id="@+id/copies_edittext"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="0"
- android:layout_column="1"
- android:minWidth="150dip"
- android:inputType="number"
- android:selectAllOnFocus="true">
- </EditText>
-
- <TextView
+ <GridLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="0"
- android:layout_column="0"
- android:text="@string/label_copies"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:labelFor="@id/copies_edittext">
- </TextView>
+ android:layout_margin="32dip"
+ android:orientation="vertical"
+ android:columnCount="2">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="1"
- android:layout_column="0"
- android:text="@string/label_destination"
- android:textAppearance="?android:attr/textAppearanceMedium">
- </TextView>
+ <!-- Destination -->
- <Spinner
- android:id="@+id/destination_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="1"
- android:layout_column="1"
- android:minWidth="150dip">
- </Spinner>
+ <Spinner
+ android:id="@+id/destination_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dip"
+ android:layout_row="0"
+ android:layout_column="0"
+ android:layout_columnSpan="2"
+ android:minWidth="324dip"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall">
+ </Spinner>
+ <!-- Copies -->
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="2"
- android:layout_column="0"
- android:text="@string/label_media_size"
- android:textAppearance="?android:attr/textAppearanceMedium">
- </TextView>
+ <view
+ class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
+ android:id="@+id/copies_edittext"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="12dip"
+ android:layout_marginBottom="12dip"
+ android:layout_row="2"
+ android:layout_column="0"
+ android:layout_gravity="bottom"
+ android:inputType="numberDecimal"
+ android:selectAllOnFocus="true"
+ android:minWidth="150dip">
+ </view>
- <Spinner
- android:id="@+id/media_size_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="2"
- android:layout_column="1"
- android:minWidth="150dip">
- </Spinner>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="1"
+ android:layout_column="0"
+ android:layout_gravity="left|bottom"
+ android:text="@string/label_copies"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ android:labelFor="@id/copies_edittext">
+ </TextView>
+ <!-- Paper size -->
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="3"
- android:layout_column="0"
- android:text="@string/label_resolution"
- android:textAppearance="?android:attr/textAppearanceMedium">
- </TextView>
+ <Spinner
+ android:id="@+id/paper_size_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginBottom="12dip"
+ android:layout_row="2"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
- <Spinner
- android:id="@+id/resolution_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="3"
- android:layout_column="1"
- android:minWidth="150dip">
- </Spinner>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginTop="12dip"
+ android:layout_row="1"
+ android:layout_column="1"
+ android:text="@string/label_paper_size"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ android:labelFor="@id/paper_size_spinner">
+ </TextView>
+ <!-- Color -->
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="4"
- android:layout_column="0"
- android:text="@string/label_input_tray"
- android:textAppearance="?android:attr/textAppearanceMedium">
- </TextView>
+ <Spinner
+ android:id="@+id/color_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="12dip"
+ android:layout_marginBottom="12dip"
+ android:layout_row="4"
+ android:layout_column="0"
+ android:minWidth="150dip">
+ </Spinner>
- <Spinner
- android:id="@+id/input_tray_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="4"
- android:layout_column="1"
- android:minWidth="150dip">
- </Spinner>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="3"
+ android:layout_column="0"
+ android:text="@string/label_color"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ android:labelFor="@id/color_spinner">
+ </TextView>
+ <!-- Orientation -->
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="5"
- android:layout_column="0"
- android:text="@string/label_output_tray"
- android:textAppearance="?android:attr/textAppearanceMedium">
- </TextView>
+ <Spinner
+ android:id="@+id/orientation_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginBottom="12dip"
+ android:layout_row="4"
+ android:layout_column="1"
+ android:minWidth="150dip">
+ </Spinner>
- <Spinner
- android:id="@+id/output_tray_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="5"
- android:layout_column="1"
- android:minWidth="150dip">
- </Spinner>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_marginTop="12dip"
+ android:layout_row="3"
+ android:layout_column="1"
+ android:text="@string/label_orientation"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ android:labelFor="@id/orientation_spinner">
+ </TextView>
+ <!-- Pages -->
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="6"
- android:layout_column="0"
- android:text="@string/label_duplex_mode"
- android:textAppearance="?android:attr/textAppearanceMedium">
- </TextView>
+ <Spinner
+ android:id="@+id/range_options_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="12dip"
+ android:layout_row="6"
+ android:layout_column="0"
+ android:minWidth="150dip">
+ </Spinner>
- <Spinner
- android:id="@+id/duplex_mode_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="6"
- android:layout_column="1"
- android:minWidth="150dip">
- </Spinner>
+ <EditText
+ android:id="@+id/page_range_edittext"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dip"
+ android:layout_row="6"
+ android:layout_column="1"
+ android:layout_gravity="bottom"
+ android:selectAllOnFocus="true"
+ android:minWidth="150dip"
+ android:hint="@string/pages_range_example"
+ android:inputType="textNoSuggestions"
+ android:visibility="gone">
+ </EditText>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dip"
+ android:layout_marginRight="12dip"
+ android:layout_row="5"
+ android:layout_column="0"
+ android:text="@string/label_pages"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ android:labelFor="@id/range_options_spinner">
+ </TextView>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="7"
- android:layout_column="0"
- android:text="@string/label_color_mode"
- android:textAppearance="?android:attr/textAppearanceMedium">
- </TextView>
+ </GridLayout>
- <Spinner
- android:id="@+id/color_mode_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="7"
- android:layout_column="1"
- android:minWidth="150dip">
- </Spinner>
+ </ScrollView>
+ <Button
+ android:id="@+id/print_button"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="0dip"
+ android:text="@string/print_button"
+ android:background="?android:attr/selectableItemBackground">
+ </Button>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="8"
- android:layout_column="0"
- android:text="@string/label_fitting_mode"
- android:textAppearance="?android:attr/textAppearanceMedium">
- </TextView>
-
- <Spinner
- android:id="@+id/fitting_mode_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="8"
- android:layout_column="1"
- android:minWidth="150dip">
- </Spinner>
-
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="9"
- android:layout_column="0"
- android:text="@string/label_orientation"
- android:textAppearance="?android:attr/textAppearanceMedium">
- </TextView>
-
- <Spinner
- android:id="@+id/orientation_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="12dip"
- android:layout_marginRight="12dip"
- android:layout_row="9"
- android:layout_column="1"
- android:minWidth="150dip">
- </Spinner>
-
- </GridLayout>
-
-</ScrollView>
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
new file mode 100644
index 0000000..66c6724
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:orientation="vertical"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textIsSelectable="false">
+ </TextView>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textIsSelectable="false"
+ android:visibility="gone">
+
+ </TextView>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/menu/print_job_config_activity.xml b/packages/PrintSpooler/res/values/constants.xml
similarity index 65%
rename from packages/PrintSpooler/res/menu/print_job_config_activity.xml
rename to packages/PrintSpooler/res/values/constants.xml
index 149c274..7d2cdc3 100644
--- a/packages/PrintSpooler/res/menu/print_job_config_activity.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -13,9 +13,15 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/print_button"
- android:title="@string/print_button"
- android:showAsAction="ifRoom">
- </item>
-</menu>
+
+<resources>
+
+ <integer name="page_option_value_all">1</integer>
+ <integer name="page_option_value_page_range">2</integer>
+
+ <integer-array name="page_options_values" translatable="false">
+ <item>@integer/page_option_value_all</item>
+ <item>@integer/page_option_value_page_range</item>
+ </integer-array>
+
+</resources>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 8b4b40a..de6fb60 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -16,74 +16,44 @@
<resources>
- <!-- Title of the PrintSpooler application. [CHAR LIMIT=16] -->
+ <!-- Title of the PrintSpooler application. [CHAR LIMIT=50] -->
<string name="app_label">Print Spooler</string>
- <!-- Title of the print dialog. [CHAR LIMIT=10] -->
- <string name="print_job_config_dialog_title">Print</string>
-
<!-- Label of the print dialog's print button. [CHAR LIMIT=16] -->
- <string name="print_button">Print</string>
+ <string name="print_button">PRINT</string>
- <!-- Label of the print dialog's cancel button. [CHAR LIMIT=16] -->
- <string name="cancel_button">Cancel</string>
+ <!-- Label of the destination widget. [CHAR LIMIT=20] -->
+ <string name="label_destination">DESTIINATION</string>
- <!-- Label of the destination spinner. [CHAR LIMIT=16] -->
- <string name="label_destination">Destination</string>
+ <!-- Label of the copies count widget. [CHAR LIMIT=20] -->
+ <string name="label_copies">COPIES</string>
- <!-- Label of the copies count edit text. [CHAR LIMIT=16] -->
- <string name="label_copies">Copies</string>
+ <!-- Label of the paper size widget. [CHAR LIMIT=20] -->
+ <string name="label_paper_size">PAPER SIZE</string>
- <!-- Label of the media size spinner. [CHAR LIMIT=16] -->
- <string name="label_media_size">Media size</string>
+ <!-- Label of the color mode widget. [CHAR LIMIT=20] -->
+ <string name="label_color">COLOR</string>
- <!-- Label of the resolution spinner. [CHAR LIMIT=16] -->
- <string name="label_resolution">Resolution</string>
+ <!-- Label of the orientation widget. [CHAR LIMIT=20] -->
+ <string name="label_orientation">ORIENTATION</string>
- <!-- Label of the input tray spinner. [CHAR LIMIT=16] -->
- <string name="label_input_tray">Input tray</string>
+ <!-- Label of the page selection widget. [CHAR LIMIT=20] -->
+ <string name="label_pages">PAGES</string>
- <!-- Label of the output tray spinner. [CHAR LIMIT=16] -->
- <string name="label_output_tray">Output tray</string>
+ <!-- Page range exmple used as a hint of how to specify such. [CHAR LIMIT=15] -->
+ <string name="pages_range_example">e.g. 1–5, 8</string>
- <!-- Label of the duplex mode spinner. [CHAR LIMIT=16] -->
- <string name="label_duplex_mode">Duplex mode</string>
-
- <!-- Label of the color mode spinner. [CHAR LIMIT=16] -->
- <string name="label_color_mode">Color mode</string>
-
- <!-- Label of the fitting mode spinner. [CHAR LIMIT=16] -->
- <string name="label_fitting_mode">Fitting mode</string>
-
- <!-- Label of the orientation spinner. [CHAR LIMIT=16] -->
- <string name="label_orientation">Orientation</string>
-
- <!-- Duplex mode labels. -->
- <string-array name="duplex_mode_labels">
- <!-- Duplex mode label: No duplexing. [CHAR LIMIT=20] -->
- <item>None</item>
- <!-- Duplex mode label: Turn a page along its long edge, e.g. like a book. [CHAR LIMIT=20] -->
- <item>Long edge</item>
- <!-- Duplex mode label: Turn a page along its short edge, e.g. like a notepad. [CHAR LIMIT=20] -->
- <item>Short edge</item>
- </string-array>
+ <!-- Message to notify the user of entering invalid input. [CHAR LIMIT=25] -->
+ <string name="invalid_input">Invalid input</string>
<!-- Color mode labels. -->
<string-array name="color_mode_labels">
<!-- Color modelabel: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] -->
- <item>Monochrome</item>
+ <item>Black & White</item>
<!-- Color mode label: Color color scheme, e.g. many colors are used. [CHAR LIMIT=20] -->
<item>Color</item>
</string-array>
- <!-- Fitting mode labels. -->
- <string-array name="fitting_mode_labels">
- <!-- Fitting mode label: No fitting. [CHAR LIMIT=30] -->
- <item>None</item>
- <!-- Fitting mode label: Fit the content to the page. [CHAR LIMIT=30] -->
- <item>Fit to page</item>
- </string-array>
-
<!-- Orientation labels. -->
<string-array name="orientation_labels">
<!-- Orientation label: Portrait page orientation. [CHAR LIMIT=30] -->
@@ -92,6 +62,14 @@
<item>Landscape</item>
</string-array>
+ <!-- Page options labels. -->
+ <string-array name="page_options_labels">
+ <!-- Page range option label: Print all pages [CHAR LIMIT=30] -->
+ <item>All</item>
+ <!-- Page range option label: Print a page range [CHAR LIMIT=30] -->
+ <item>Range</item>
+ </string-array>
+
<!-- Title of an application permission, listed so the user can choose
whether they want to allow the application to do this. -->
<string name="permlab_bindPrintSpoolerService">bind to a print spooler service</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 659102c..19f545d 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -17,7 +17,12 @@
package com.android.printspooler;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
@@ -34,8 +39,6 @@
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintAttributes.MediaSize;
-import android.print.PrintAttributes.Resolution;
-import android.print.PrintAttributes.Tray;
import android.print.PrintDocumentAdapter.LayoutResultCallback;
import android.print.PrintDocumentAdapter.WriteResultCallback;
import android.print.PrintDocumentInfo;
@@ -43,25 +46,28 @@
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.text.Editable;
-import android.text.InputFilter;
-import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
+import android.util.AttributeSet;
import android.util.Log;
-import android.view.Choreographer;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
+import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
+import android.widget.TextView;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Activity for configuring a print job.
@@ -79,6 +85,14 @@
private static final int MIN_COPIES = 1;
+ private static final Pattern PATTERN_DIGITS = Pattern.compile("\\d");
+
+ private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile(
+ "(?=[]\\[+&|!(){}^\"~*?:\\\\])");
+
+ private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
+ "([0-9]+[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*[,]?[\\s]*)+");
+
private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
@@ -90,42 +104,36 @@
private RemotePrintDocumentAdapter mRemotePrintAdapter;
+ private boolean mPrintConfirmed;
+
+ private boolean mStarted;
+
+ private IBinder mIPrintDocumentAdapter;
+
+ private PrintDocumentInfo mPrintDocumentInfo;
+
// UI elements
private EditText mCopiesEditText;
+ private EditText mRangeEditText;
+
private Spinner mDestinationSpinner;
public ArrayAdapter<SpinnerItem<PrinterInfo>> mDestinationSpinnerAdapter;
private Spinner mMediaSizeSpinner;
public ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
- private Spinner mResolutionSpinner;
- public ArrayAdapter<SpinnerItem<Resolution>> mResolutionSpinnerAdapter;
-
- private Spinner mInputTraySpinner;
- public ArrayAdapter<SpinnerItem<Tray>> mInputTraySpinnerAdapter;
-
- private Spinner mOutputTraySpinner;
- public ArrayAdapter<SpinnerItem<Tray>> mOutputTraySpinnerAdapter;
-
- private Spinner mDuplexModeSpinner;
- public ArrayAdapter<SpinnerItem<Integer>> mDuplexModeSpinnerAdapter;
-
private Spinner mColorModeSpinner;
public ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
- private Spinner mFittingModeSpinner;
- public ArrayAdapter<SpinnerItem<Integer>> mFittingModeSpinnerAdapter;
-
private Spinner mOrientationSpinner;
public ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
- private boolean mPrintConfirmed;
+ private Spinner mRangeOptionsSpinner;
+ public ArrayAdapter<SpinnerItem<Integer>> mRangeOptionsSpinnerAdapter;
- private boolean mStarted;
-
- private IBinder mIPrintDocumentAdapter;
+ private Button mPrintButton;
// TODO: Implement store/restore state.
@@ -140,35 +148,34 @@
SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
mPrintAttributes.setMediaSize(mediaItem.value);
updatePrintableContentIfNeeded();
- } else if (spinner == mResolutionSpinner) {
- SpinnerItem<Resolution> resolutionItem =
- mResolutionSpinnerAdapter.getItem(position);
- mPrintAttributes.setResolution(resolutionItem.value);
- updatePrintableContentIfNeeded();
- } else if (spinner == mInputTraySpinner) {
- SpinnerItem<Tray> inputTrayItem =
- mInputTraySpinnerAdapter.getItem(position);
- mPrintAttributes.setInputTray(inputTrayItem.value);
- } else if (spinner == mOutputTraySpinner) {
- SpinnerItem<Tray> outputTrayItem =
- mOutputTraySpinnerAdapter.getItem(position);
- mPrintAttributes.setOutputTray(outputTrayItem.value);
- } else if (spinner == mDuplexModeSpinner) {
- SpinnerItem<Integer> duplexModeItem =
- mDuplexModeSpinnerAdapter.getItem(position);
- mPrintAttributes.setDuplexMode(duplexModeItem.value);
} else if (spinner == mColorModeSpinner) {
SpinnerItem<Integer> colorModeItem =
mColorModeSpinnerAdapter.getItem(position);
mPrintAttributes.setColorMode(colorModeItem.value);
- } else if (spinner == mFittingModeSpinner) {
- SpinnerItem<Integer> fittingModeItem =
- mFittingModeSpinnerAdapter.getItem(position);
- mPrintAttributes.setFittingMode(fittingModeItem.value);
} else if (spinner == mOrientationSpinner) {
SpinnerItem<Integer> orientationItem =
mOrientationSpinnerAdapter.getItem(position);
mPrintAttributes.setOrientation(orientationItem.value);
+ } else if (spinner == mRangeOptionsSpinner) {
+ SpinnerItem<Integer> rangeOptionItem =
+ mRangeOptionsSpinnerAdapter.getItem(position);
+ if (rangeOptionItem.value == getResources().getInteger(
+ R.integer.page_option_value_all)) {
+ mRangeEditText.setVisibility(View.INVISIBLE);
+ mRangeEditText.setEnabled(false);
+ mRangeEditText.setText(null);
+ mRangeEditText.setError(null);
+ mPrintButton.setEnabled(true);
+ } else if (rangeOptionItem.value == getResources().getInteger(
+ R.integer.page_option_value_page_range)) {
+ mRangeEditText.setVisibility(View.VISIBLE);
+ mRangeEditText.setEnabled(true);
+ mRangeEditText.requestFocus();
+ mRangeEditText.setError(getString(R.string.invalid_input));
+ InputMethodManager imm = (InputMethodManager)
+ getSystemService(INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mRangeEditText, 0);
+ }
}
}
@@ -178,11 +185,10 @@
}
};
- private final TextWatcher mTextWatcher = new TextWatcher() {
+ private final TextWatcher mCopiesTextWatcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- final int copies = Integer.parseInt(mCopiesEditText.getText().toString());
- mPrintAttributes.setCopies(copies);
+ /* do nothing */
}
@Override
@@ -191,25 +197,64 @@
}
@Override
- public void afterTextChanged(Editable s) {
- /* do nothing */
+ public void afterTextChanged(Editable editable) {
+ if (editable.length() == 0) {
+ mCopiesEditText.setError(getString(R.string.invalid_input));
+ mPrintButton.setEnabled(false);
+ return;
+ }
+ final int copies = Integer.parseInt(editable.toString());
+ if (copies < MIN_COPIES) {
+ mCopiesEditText.setError(getString(R.string.invalid_input));
+ mPrintButton.setEnabled(false);
+ return;
+ }
+ mPrintAttributes.setCopies(copies);
+ mPrintButton.setEnabled(true);
}
};
- private final InputFilter mInputFilter = new InputFilter() {
+ private final TextWatcher mRangeTextWatcher = new TextWatcher() {
@Override
- public CharSequence filter(CharSequence source, int start, int end,
- Spanned dest, int dstart, int dend) {
- StringBuffer text = new StringBuffer(dest.toString());
- text.replace(dstart, dend, source.subSequence(start, end).toString());
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ /* do nothing */
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ /* do nothing */
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ String text = editable.toString();
+
if (TextUtils.isEmpty(text)) {
- return dest;
+ mRangeEditText.setError(getString(R.string.invalid_input));
+ mPrintButton.setEnabled(false);
+ return;
}
- final int copies = Integer.parseInt(text.toString());
- if (copies < MIN_COPIES) {
- return dest;
+
+ String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////");
+ if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) {
+ mRangeEditText.setError(getString(R.string.invalid_input));
+ mPrintButton.setEnabled(false);
+ return;
}
- return null;
+
+ Matcher matcher = PATTERN_DIGITS.matcher(text);
+ while (matcher.find()) {
+ String numericString = text.substring(matcher.start(), matcher.end());
+ final int pageIndex = Integer.parseInt(numericString);
+ if (pageIndex < 1 || pageIndex > mPrintDocumentInfo.getPageCount()) {
+ mRangeEditText.setError(getString(R.string.invalid_input));
+ mPrintButton.setEnabled(false);
+ return;
+ }
+ }
+
+ mRangeEditText.setError(null);
+ mPrintButton.setEnabled(true);
}
};
@@ -274,70 +319,95 @@
// Copies
mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
mCopiesEditText.setText(String.valueOf(MIN_COPIES));
- mCopiesEditText.addTextChangedListener(mTextWatcher);
- mCopiesEditText.setFilters(new InputFilter[] {mInputFilter});
+ mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
// Destination.
mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
mDestinationSpinnerAdapter = new ArrayAdapter<SpinnerItem<PrinterInfo>>(this,
- android.R.layout.simple_spinner_dropdown_item);
+ R.layout.spinner_dropdown_item) {
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ return getView(position, convertView, parent);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.spinner_dropdown_item, parent, false);
+ }
+
+ PrinterInfo printerInfo = getItem(position).value;
+ TextView title = (TextView) convertView.findViewById(R.id.title);
+ title.setText(printerInfo.getLabel());
+
+ try {
+ TextView subtitle = (TextView) convertView.findViewById(R.id.subtitle);
+ PackageManager pm = getPackageManager();
+ PackageInfo packageInfo = pm.getPackageInfo(
+ printerInfo.getId().getService().getPackageName(), 0);
+ subtitle.setText(packageInfo.applicationInfo.loadLabel(pm));
+ subtitle.setVisibility(View.VISIBLE);
+ } catch (NameNotFoundException nnfe) {
+ /* ignore */
+ }
+
+ return convertView;
+ }
+ };
mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
// Media size.
- mMediaSizeSpinner = (Spinner) findViewById(R.id.media_size_spinner);
+ mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
mMediaSizeSpinnerAdapter = new ArrayAdapter<SpinnerItem<MediaSize>>(this,
- android.R.layout.simple_spinner_dropdown_item);
+ R.layout.spinner_dropdown_item, R.id.title);
mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
- // Resolution.
- mResolutionSpinner = (Spinner) findViewById(R.id.resolution_spinner);
- mResolutionSpinnerAdapter = new ArrayAdapter<SpinnerItem<Resolution>>(this,
- android.R.layout.simple_spinner_dropdown_item);
- mResolutionSpinner.setAdapter(mResolutionSpinnerAdapter);
- mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
- // Input tray.
- mInputTraySpinner = (Spinner) findViewById(R.id.input_tray_spinner);
- mInputTraySpinnerAdapter = new ArrayAdapter<SpinnerItem<Tray>>(this,
- android.R.layout.simple_spinner_dropdown_item);
- mInputTraySpinner.setAdapter(mInputTraySpinnerAdapter);
-
- // Output tray.
- mOutputTraySpinner = (Spinner) findViewById(R.id.output_tray_spinner);
- mOutputTraySpinnerAdapter = new ArrayAdapter<SpinnerItem<Tray>>(this,
- android.R.layout.simple_spinner_dropdown_item);
- mOutputTraySpinner.setAdapter(mOutputTraySpinnerAdapter);
- mOutputTraySpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
- // Duplex mode.
- mDuplexModeSpinner = (Spinner) findViewById(R.id.duplex_mode_spinner);
- mDuplexModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
- android.R.layout.simple_spinner_dropdown_item);
- mDuplexModeSpinner.setAdapter(mDuplexModeSpinnerAdapter);
- mDuplexModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
// Color mode.
- mColorModeSpinner = (Spinner) findViewById(R.id.color_mode_spinner);
+ mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
mColorModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
- android.R.layout.simple_spinner_dropdown_item);
+ R.layout.spinner_dropdown_item, R.id.title);
mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
- // Color mode.
- mFittingModeSpinner = (Spinner) findViewById(R.id.fitting_mode_spinner);
- mFittingModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
- android.R.layout.simple_spinner_dropdown_item);
- mFittingModeSpinner.setAdapter(mFittingModeSpinnerAdapter);
- mFittingModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
// Orientation
mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner);
mOrientationSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
- android.R.layout.simple_spinner_dropdown_item);
+ R.layout.spinner_dropdown_item, R.id.title);
mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter);
mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Range
+ mRangeEditText = (EditText) findViewById(R.id.page_range_edittext);
+ mRangeEditText.addTextChangedListener(mRangeTextWatcher);
+
+ // Range options
+ mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
+ mRangeOptionsSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+ R.layout.spinner_dropdown_item, R.id.title);
+ mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter);
+ mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ final int[] rangeOptionsValues = getResources().getIntArray(
+ R.array.page_options_values);
+ String[] rangeOptionsLabels = getResources().getStringArray(
+ R.array.page_options_labels);
+ final int rangeOptionsCount = rangeOptionsLabels.length;
+ for (int i = 0; i < rangeOptionsCount; i++) {
+ mRangeOptionsSpinnerAdapter.add(new SpinnerItem<Integer>(
+ rangeOptionsValues[i], rangeOptionsLabels[i]));
+ }
+ mRangeOptionsSpinner.setSelection(0);
+
+ mPrintButton = (Button) findViewById(R.id.print_button);
+ mPrintButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPrintConfirmed = true;
+ finish();
+ }
+ });
}
private void updateUi() {
@@ -348,6 +418,7 @@
// Copies.
mCopiesEditText.setText(String.valueOf(
Math.max(mPrintAttributes.getCopies(), MIN_COPIES)));
+ mCopiesEditText.selectAll();
// Media size.
mMediaSizeSpinnerAdapter.clear();
@@ -363,80 +434,6 @@
mMediaSizeSpinner.setOnItemSelectedListener(null);
mMediaSizeSpinner.setSelection(selectedMediaSizeIndex);
- // Resolution.
- mResolutionSpinnerAdapter.clear();
- List<Resolution> resolutions = printer.getResolutions();
- final int resolutionCount = resolutions.size();
- for (int i = 0; i < resolutionCount; i++) {
- Resolution resolution = resolutions.get(i);
- mResolutionSpinnerAdapter.add(new SpinnerItem<Resolution>(
- resolution, resolution.getLabel(getPackageManager())));
- }
- final int selectedResolutionIndex = resolutions.indexOf(
- mPrintAttributes.getResolution());
- mResolutionSpinner.setOnItemSelectedListener(null);
- mResolutionSpinner.setSelection(selectedResolutionIndex);
-
- // AdapterView has the weird behavior to notify the selection listener for a
- // selection event that occurred *before* the listener was registered because
- // it does the real selection change on the next layout pass. To avoid this
- // behavior we re-attach the listener in the next traversal window - fun!
- Choreographer.getInstance().postCallback(
- Choreographer.CALLBACK_TRAVERSAL, new Runnable() {
- @Override
- public void run() {
- mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
- mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
- }
- }, null);
-
- // Input tray.
- mInputTraySpinnerAdapter.clear();
- List<Tray> inputTrays = printer.getInputTrays();
- if (inputTrays != null) {
- final int inputTrayCount = inputTrays.size();
- for (int i = 0; i < inputTrayCount; i++) {
- Tray inputTray = inputTrays.get(i);
- mInputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
- inputTray, inputTray.getLabel(getPackageManager())));
- }
- final int selectedInputTrayIndex = inputTrays.indexOf(
- mPrintAttributes.getInputTray());
- mInputTraySpinner.setSelection(selectedInputTrayIndex);
- }
-
- // Output tray.
- mOutputTraySpinnerAdapter.clear();
- List<Tray> outputTrays = printer.getOutputTrays();
- if (outputTrays != null) {
- final int outputTrayCount = outputTrays.size();
- for (int i = 0; i < outputTrayCount; i++) {
- Tray outputTray = outputTrays.get(i);
- mOutputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
- outputTray, outputTray.getLabel(getPackageManager())));
- }
- final int selectedOutputTrayIndex = outputTrays.indexOf(
- mPrintAttributes.getOutputTray());
- mOutputTraySpinner.setSelection(selectedOutputTrayIndex);
- }
-
- // Duplex mode.
- final int duplexModes = printer.getDuplexModes();
- mDuplexModeSpinnerAdapter.clear();
- String[] duplexModeLabels = getResources().getStringArray(
- R.array.duplex_mode_labels);
- int remainingDuplexModes = duplexModes;
- while (remainingDuplexModes != 0) {
- final int duplexBitOffset = Integer.numberOfTrailingZeros(remainingDuplexModes);
- final int duplexMode = 1 << duplexBitOffset;
- remainingDuplexModes &= ~duplexMode;
- mDuplexModeSpinnerAdapter.add(new SpinnerItem<Integer>(duplexMode,
- duplexModeLabels[duplexBitOffset]));
- }
- final int selectedDuplexModeIndex = Integer.numberOfTrailingZeros(
- (duplexModes & mPrintAttributes.getDuplexMode()));
- mDuplexModeSpinner.setSelection(selectedDuplexModeIndex);
-
// Color mode.
final int colorModes = printer.getColorModes();
mColorModeSpinnerAdapter.clear();
@@ -454,23 +451,6 @@
(colorModes & mPrintAttributes.getColorMode()));
mColorModeSpinner.setSelection(selectedColorModeIndex);
- // Fitting mode.
- final int fittingModes = printer.getFittingModes();
- mFittingModeSpinnerAdapter.clear();
- String[] fittingModeLabels = getResources().getStringArray(
- R.array.fitting_mode_labels);
- int remainingFittingModes = fittingModes;
- while (remainingFittingModes != 0) {
- final int fittingBitOffset = Integer.numberOfTrailingZeros(remainingFittingModes);
- final int fittingMode = 1 << fittingBitOffset;
- remainingFittingModes &= ~fittingMode;
- mFittingModeSpinnerAdapter.add(new SpinnerItem<Integer>(fittingMode,
- fittingModeLabels[fittingBitOffset]));
- }
- final int selectedFittingModeIndex = Integer.numberOfTrailingZeros(
- (fittingModes & mPrintAttributes.getFittingMode()));
- mFittingModeSpinner.setSelection(selectedFittingModeIndex);
-
// Orientation.
final int orientations = printer.getOrientations();
mOrientationSpinnerAdapter.clear();
@@ -503,21 +483,6 @@
notifyPrintableFinishIfNeeded();
}
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.print_job_config_activity, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == R.id.print_button) {
- mPrintConfirmed = true;
- finish();
- }
- return super.onOptionsItemSelected(item);
- }
-
private void notifyPrintableStartIfNeeded() {
if (mDestinationSpinner.getSelectedItemPosition() < 0
|| mStarted) {
@@ -536,10 +501,13 @@
// TODO: Implement old attributes tracking
mPrintSpooler.setPrintJobAttributes(mPrintJobId, mPrintAttributes);
+ // TODO: Implement setting the print preview attribute
mRemotePrintAdapter.layout(new PrintAttributes.Builder().create(),
mPrintAttributes, new LayoutResultCallback() {
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+ mPrintDocumentInfo = info;
+
// TODO: Handle the case of unchanged content
mPrintSpooler.setPrintJobPrintDocumentInfo(mPrintJobId, info);
@@ -566,7 +534,7 @@
Log.e(LOG_TAG, "Error during layout: " + error);
finishActivity(Activity.RESULT_CANCELED);
}
- });
+ }, new Bundle());
}
private void notifyPrintableFinishIfNeeded() {
@@ -574,10 +542,7 @@
return;
}
- if (!mPrintConfirmed) {
- mRemotePrintAdapter.cancel();
- }
- mRemotePrintAdapter.finish();
+ mRemotePrintAdapter.finish(!mPrintConfirmed);
// If canceled or no printer, nothing to do.
final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
@@ -734,4 +699,38 @@
return label.toString();
}
}
+
+ /**
+ * An instance of this class class is intended to be the first focusable
+ * in a layout to which the system automatically gives focus. It performs
+ * some voodoo to avoid the first tap on it to start an edit mode, rather
+ * to bring up the IME, i.e. to get the behavior as if the view was not
+ * focused.
+ */
+ public static final class CustomEditText extends EditText {
+ private boolean mClickedBeforeFocus;
+
+ public CustomEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean performClick() {
+ super.performClick();
+ if (isFocused() && !mClickedBeforeFocus) {
+ clearFocus();
+ requestFocus();
+ }
+ mClickedBeforeFocus = true;
+ return true;
+ }
+
+ protected void onFocusChanged(boolean gainFocus, int direction,
+ Rect previouslyFocusedRect) {
+ if (!gainFocus) {
+ mClickedBeforeFocus = false;
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+ }
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
index 0546a43..53ae1459 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
@@ -24,10 +24,15 @@
import android.print.IPrintClient;
import android.print.IPrintSpoolerClient;
import android.print.IPrinterDiscoveryObserver;
+import android.print.PageRange;
import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintAttributes.Tray;
+import android.print.PrintDocumentInfo;
import android.print.PrintJobInfo;
import android.print.PrintManager;
-import android.print.PrintDocumentInfo;
import android.print.PrinterId;
import android.util.AtomicFile;
import android.util.Log;
@@ -58,9 +63,9 @@
private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
- private static final boolean DEBUG_PERSISTENCE = false;
+ private static final boolean DEBUG_PERSISTENCE = true;
- private static final boolean PERSISTNECE_MANAGER_ENABLED = false;
+ private static final boolean PERSISTNECE_MANAGER_ENABLED = true;
private static final String PRINT_FILE_EXTENSION = "pdf";
@@ -91,8 +96,7 @@
private PrintSpooler(Context context) {
mContext = context;
- mPersistanceManager = new PersistenceManager();
- mPersistanceManager.readStateLocked();
+ mPersistanceManager = new PersistenceManager(context);
}
public void setCleint(IPrintSpoolerClient client) {
@@ -101,6 +105,12 @@
}
}
+ public void restorePersistedState() {
+ synchronized (mLock) {
+ mPersistanceManager.readStateLocked();
+ }
+ }
+
public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
IPrintSpoolerClient client = null;
synchronized (mLock) {
@@ -129,7 +139,7 @@
}
}
- public List<PrintJobInfo> getPrintJobs(ComponentName componentName, int state, int appId) {
+ public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state, int appId) {
synchronized (mLock) {
List<PrintJobInfo> foundPrintJobs = null;
final int printJobCount = mPrintJobs.size();
@@ -141,8 +151,10 @@
&& componentName.equals(printerId.getService())));
final boolean sameAppId = appId == PrintManager.APP_ID_ANY
|| printJob.getAppId() == appId;
- final boolean sameState = state == PrintJobInfo.STATE_ANY
- || state == printJob.getState();
+ final boolean sameState = (state == printJob.getState())
+ || (state == PrintJobInfo.STATE_ANY)
+ || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
+ && printJob.getState() > PrintJobInfo.STATE_CREATED);
if (sameComponent && sameAppId && sameState) {
if (foundPrintJobs == null) {
foundPrintJobs = new ArrayList<PrintJobInfo>();
@@ -154,7 +166,7 @@
}
}
- public PrintJobInfo getPrintJob(int printJobId, int appId) {
+ public PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
synchronized (mLock) {
final int printJobCount = mPrintJobs.size();
for (int i = 0; i < printJobCount; i++) {
@@ -170,7 +182,7 @@
public boolean cancelPrintJob(int printJobId, int appId) {
synchronized (mLock) {
- PrintJobInfo printJob = getPrintJob(printJobId, appId);
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, appId);
if (printJob != null) {
switch (printJob.getState()) {
case PrintJobInfo.STATE_CREATED:
@@ -192,9 +204,9 @@
printJob.setAppId(appId);
printJob.setLabel(label);
printJob.setAttributes(attributes);
+ printJob.setState(PrintJobInfo.STATE_CREATED);
addPrintJobLocked(printJob);
- setPrintJobState(printJobId, PrintJobInfo.STATE_CREATED);
return printJob;
}
@@ -291,7 +303,7 @@
FileInputStream in = null;
FileOutputStream out = null;
try {
- PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null) {
File file = generateFileForPrintJob(printJobId);
in = new FileInputStream(file);
@@ -355,7 +367,7 @@
}
client = mClient;
- PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null && printJob.getState() < state) {
success = true;
printJob.setState(state);
@@ -425,12 +437,22 @@
}
}
- private void callOnAllPrintJobsHandledQuietly(IPrintSpoolerClient client) {
- try {
- client.onAllPrintJobsHandled();
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
- }
+ private void callOnAllPrintJobsHandledQuietly(final IPrintSpoolerClient client) {
+ // This has to run on the tread that is persisting the current state
+ // since this call may result in the system unbinding from the spooler
+ // and as a result the spooler process may get killed before the write
+ // completes.
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ client.onAllPrintJobsHandled();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
+ }
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
}
private boolean hasActivePrintJobsLocked() {
@@ -465,7 +487,7 @@
public boolean setPrintJobTag(int printJobId, String tag) {
synchronized (mLock) {
- PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null) {
printJob.setTag(tag);
mPersistanceManager.writeStateLocked();
@@ -477,7 +499,7 @@
public final boolean setPrintJobPrintDocumentInfo(int printJobId, PrintDocumentInfo info) {
synchronized (mLock) {
- PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null) {
printJob.setDocumentInfo(info);
mPersistanceManager.writeStateLocked();
@@ -489,7 +511,7 @@
public void setPrintJobAttributes(int printJobId, PrintAttributes attributes) {
synchronized (mLock) {
- PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null) {
printJob.setAttributes(attributes);
mPersistanceManager.writeStateLocked();
@@ -499,7 +521,7 @@
public void setPrintJobPrinterId(int printJobId, PrinterId printerId) {
synchronized (mLock) {
- PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
if (printJob != null) {
printJob.setPrinterId(printerId);
mPersistanceManager.writeStateLocked();
@@ -507,40 +529,77 @@
}
}
+ public boolean setPrintJobPages(int printJobId, PageRange[] pages) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ printJob.setPages(pages);
+ mPersistanceManager.writeStateLocked();
+ return true;
+ }
+ }
+ return false;
+ }
+
private final class PersistenceManager {
private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
private static final String TAG_SPOOLER = "spooler";
private static final String TAG_JOB = "job";
- private static final String TAG_ID = "id";
- private static final String TAG_TAG = "tag";
- private static final String TAG_APP_ID = "app-id";
- private static final String TAG_STATE = "state";
- private static final String TAG_ATTRIBUTES = "attributes";
- private static final String TAG_LABEL = "label";
- private static final String TAG_PRINTER = "printer";
- private static final String ATTRIBUTE_MEDIA_SIZE = "mediaSize";
- private static final String ATTRIBUTE_RESOLUTION = "resolution";
- private static final String ATTRIBUTE_MARGINS = "margins";
- private static final String ATTRIBUTE_INPUT_TRAY = "inputTray";
- private static final String ATTRIBUTE_OUTPUT_TRAY = "outputTray";
- private static final String ATTRIBUTE_DUPLEX_MODE = "duplexMode";
- private static final String ATTRIBUTE_COLOR_MODE = "colorMode";
- private static final String ATTRIBUTE_FITTING_MODE = "fittingMode";
- private static final String ATTRIBUTE_ORIENTATION = "orientation";
+ private static final String TAG_PRINTER_ID = "printerId";
+ private static final String TAG_PAGE_RANGE = "pageRange";
+ private static final String TAG_ATTRIBUTES = "attributes";
+ private static final String TAG_DOCUMENT_INFO = "documentInfo";
+
+ private static final String ATTR_ID = "id";
+ private static final String ATTR_LABEL = "label";
+ private static final String ATTR_STATE = "state";
+ private static final String ATTR_APP_ID = "appId";
+ private static final String ATTR_USER_ID = "userId";
+ private static final String ATTR_TAG = "tag";
+
+ private static final String TAG_MEDIA_SIZE = "mediaSize";
+ private static final String TAG_RESOLUTION = "resolution";
+ private static final String TAG_MARGINS = "margins";
+ private static final String TAG_INPUT_TRAY = "inputTray";
+ private static final String TAG_OUTPUT_TRAY = "outputTray";
+
+ private static final String ATTR_DUPLEX_MODE = "duplexMode";
+ private static final String ATTR_COLOR_MODE = "colorMode";
+ private static final String ATTR_FITTING_MODE = "fittingMode";
+ private static final String ATTR_ORIENTATION = "orientation";
+
+ private static final String ATTR_LOCAL_ID = "localId";
+ private static final String ATTR_SERVICE = "service";
+
+ private static final String ATTR_WIDTH_MILS = "widthMils";
+ private static final String ATTR_HEIGHT_MILS = "heightMils";
+
+ private static final String ATTR_HORIZONTAL_DPI = "horizontalDip";
+ private static final String ATTR_VERTICAL_DPI = "verticalDpi";
+
+ private static final String ATTR_LEFT_MILS = "leftMils";
+ private static final String ATTR_TOP_MILS = "topMils";
+ private static final String ATTR_RIGHT_MILS = "rightMils";
+ private static final String ATTR_BOTTOM_MILS = "bottomMils";
+
+ private static final String ATTR_START = "start";
+ private static final String ATTR_END = "end";
+
+ private static final String ATTR_PAGE_COUNT = "pageCount";
+ private static final String ATTR_CONTENT_TYPE = "contentType";
private final AtomicFile mStatePersistFile;
private boolean mWriteStateScheduled;
- private PersistenceManager() {
- mStatePersistFile = new AtomicFile(new File(mContext.getFilesDir(),
+ private PersistenceManager(Context context) {
+ mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
PERSIST_FILE_NAME));
}
public void writeStateLocked() {
- // TODO: Implement persistence of PrintableInfo
if (!PERSISTNECE_MANAGER_ENABLED) {
return;
}
@@ -578,99 +637,134 @@
final int state = printJob.getState();
if (state < PrintJobInfo.STATE_QUEUED
- || state > PrintJobInfo.STATE_FAILED) {
+ || state > PrintJobInfo.STATE_CANCELED) {
continue;
}
serializer.startTag(null, TAG_JOB);
- serializer.startTag(null, TAG_ID);
- serializer.text(String.valueOf(printJob.getId()));
- serializer.endTag(null, TAG_ID);
+ serializer.attribute(null, ATTR_ID, String.valueOf(printJob.getId()));
+ serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString());
+ serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState()));
+ serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId()));
+ serializer.attribute(null, ATTR_USER_ID, String.valueOf(printJob.getUserId()));
+ String tag = printJob.getTag();
+ if (tag != null) {
+ serializer.attribute(null, ATTR_TAG, tag);
+ }
- serializer.startTag(null, TAG_TAG);
- serializer.text(printJob.getTag());
- serializer.endTag(null, TAG_TAG);
+ PrinterId printerId = printJob.getPrinterId();
+ if (printerId != null) {
+ serializer.startTag(null, TAG_PRINTER_ID);
+ serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
+ serializer.attribute(null, ATTR_SERVICE, printerId.getService()
+ .flattenToString());
+ serializer.endTag(null, TAG_PRINTER_ID);
+ }
- serializer.startTag(null, TAG_APP_ID);
- serializer.text(String.valueOf(printJob.getAppId()));
- serializer.endTag(null, TAG_APP_ID);
-
- serializer.startTag(null, TAG_LABEL);
- serializer.text(printJob.getLabel().toString());
- serializer.endTag(null, TAG_LABEL);
-
- serializer.startTag(null, TAG_STATE);
- serializer.text(String.valueOf(printJob.getState()));
- serializer.endTag(null, TAG_STATE);
-
- serializer.startTag(null, TAG_PRINTER);
- serializer.text(printJob.getPrinterId().flattenToString());
- serializer.endTag(null, TAG_PRINTER);
+ PageRange[] pages = printJob.getPages();
+ if (pages != null) {
+ for (int i = 0; i < pages.length; i++) {
+ serializer.startTag(null, TAG_PAGE_RANGE);
+ serializer.attribute(null, ATTR_START, String.valueOf(
+ pages[i].getStart()));
+ serializer.attribute(null, ATTR_END, String.valueOf(
+ pages[i].getEnd()));
+ serializer.endTag(null, TAG_PAGE_RANGE);
+ }
+ }
PrintAttributes attributes = printJob.getAttributes();
if (attributes != null) {
serializer.startTag(null, TAG_ATTRIBUTES);
- //TODO: Implement persistence of the attributes below.
-
-// MediaSize mediaSize = attributes.getMediaSize();
-// if (mediaSize != null) {
-// serializer.attribute(null, ATTRIBUTE_MEDIA_SIZE,
-// mediaSize.flattenToString());
-// }
-//
-// Resolution resolution = attributes.getResolution();
-// if (resolution != null) {
-// serializer.attribute(null, ATTRIBUTE_RESOLUTION,
-// resolution.flattenToString());
-// }
-//
-// Margins margins = attributes.getMargins();
-// if (margins != null) {
-// serializer.attribute(null, ATTRIBUTE_MARGINS,
-// margins.flattenToString());
-// }
-//
-// Tray inputTray = attributes.getInputTray();
-// if (inputTray != null) {
-// serializer.attribute(null, ATTRIBUTE_INPUT_TRAY,
-// inputTray.flattenToString());
-// }
-//
-// Tray outputTray = attributes.getOutputTray();
-// if (outputTray != null) {
-// serializer.attribute(null, ATTRIBUTE_OUTPUT_TRAY,
-// outputTray.flattenToString());
-// }
-
final int duplexMode = attributes.getDuplexMode();
- if (duplexMode > 0) {
- serializer.attribute(null, ATTRIBUTE_DUPLEX_MODE,
- String.valueOf(duplexMode));
- }
+ serializer.attribute(null, ATTR_DUPLEX_MODE,
+ String.valueOf(duplexMode));
final int colorMode = attributes.getColorMode();
- if (colorMode > 0) {
- serializer.attribute(null, ATTRIBUTE_COLOR_MODE,
- String.valueOf(colorMode));
- }
+ serializer.attribute(null, ATTR_COLOR_MODE,
+ String.valueOf(colorMode));
final int fittingMode = attributes.getFittingMode();
- if (fittingMode > 0) {
- serializer.attribute(null, ATTRIBUTE_FITTING_MODE,
- String.valueOf(fittingMode));
- }
+ serializer.attribute(null, ATTR_FITTING_MODE,
+ String.valueOf(fittingMode));
final int orientation = attributes.getOrientation();
- if (orientation > 0) {
- serializer.attribute(null, ATTRIBUTE_ORIENTATION,
- String.valueOf(orientation));
+ serializer.attribute(null, ATTR_ORIENTATION,
+ String.valueOf(orientation));
+
+ MediaSize mediaSize = attributes.getMediaSize();
+ if (mediaSize != null) {
+ serializer.startTag(null, TAG_MEDIA_SIZE);
+ serializer.attribute(null, ATTR_ID, mediaSize.getId());
+ serializer.attribute(null, ATTR_LABEL, mediaSize.getLabel()
+ .toString());
+ serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf(
+ mediaSize.getWidthMils()));
+ serializer.attribute(null, ATTR_HEIGHT_MILS,String.valueOf(
+ mediaSize.getHeightMils()));
+ serializer.endTag(null, TAG_MEDIA_SIZE);
+ }
+
+ Resolution resolution = attributes.getResolution();
+ if (resolution != null) {
+ serializer.startTag(null, TAG_RESOLUTION);
+ serializer.attribute(null, ATTR_ID, resolution.getId());
+ serializer.attribute(null, ATTR_LABEL, resolution.getLabel()
+ .toString());
+ serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf(
+ resolution.getHorizontalDpi()));
+ serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf(
+ resolution.getVerticalDpi()));
+ serializer.endTag(null, TAG_RESOLUTION);
+ }
+
+ Margins margins = attributes.getMargins();
+ if (margins != null) {
+ serializer.startTag(null, TAG_MARGINS);
+ serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf(
+ margins.getLeftMils()));
+ serializer.attribute(null, ATTR_TOP_MILS, String.valueOf(
+ margins.getTopMils()));
+ serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf(
+ margins.getRightMils()));
+ serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf(
+ margins.getBottomMils()));
+ serializer.endTag(null, TAG_MARGINS);
+ }
+
+ Tray inputTray = attributes.getInputTray();
+ if (inputTray != null) {
+ serializer.startTag(null, TAG_INPUT_TRAY);
+ serializer.attribute(null, ATTR_ID, inputTray.getId());
+ serializer.attribute(null, ATTR_LABEL, inputTray.getLabel()
+ .toString());
+ serializer.endTag(null, TAG_INPUT_TRAY);
+ }
+
+ Tray outputTray = attributes.getOutputTray();
+ if (outputTray != null) {
+ serializer.startTag(null, TAG_OUTPUT_TRAY);
+ serializer.attribute(null, ATTR_ID, outputTray.getId());
+ serializer.attribute(null, ATTR_LABEL, outputTray.getLabel()
+ .toString());
+ serializer.endTag(null, TAG_OUTPUT_TRAY);
}
serializer.endTag(null, TAG_ATTRIBUTES);
}
+ PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
+ if (documentInfo != null) {
+ serializer.startTag(null, TAG_DOCUMENT_INFO);
+ serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
+ documentInfo.getContentType()));
+ serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
+ documentInfo.getPageCount()));
+ serializer.endTag(null, TAG_DOCUMENT_INFO);
+ }
+
serializer.endTag(null, TAG_JOB);
if (DEBUG_PERSISTENCE) {
@@ -752,125 +846,169 @@
if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
return false;
}
- parser.next();
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_ID);
- parser.next();
- final int printJobId = Integer.parseInt(parser.getText());
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_ID);
- parser.next();
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_TAG);
- parser.next();
- String tag = parser.getText();
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_TAG);
- parser.next();
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_APP_ID);
- parser.next();
- final int appId = Integer.parseInt(parser.getText());
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_APP_ID);
- parser.next();
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_LABEL);
- parser.next();
- String label = parser.getText();
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_LABEL);
- parser.next();
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_STATE);
- parser.next();
- final int state = Integer.parseInt(parser.getText());
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_STATE);
- parser.next();
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_PRINTER);
- parser.next();
- PrinterId printerId = PrinterId.unflattenFromString(parser.getText());
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);
- parser.next();
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES);
-
- final int attributeCount = parser.getAttributeCount();
- PrintAttributes attributes = null;
- if (attributeCount > 0) {
- PrintAttributes.Builder builder = new PrintAttributes.Builder();
-
- // TODO: Implement reading of the attributes below.
-
-// String mediaSize = parser.getAttributeValue(null, ATTRIBUTE_MEDIA_SIZE);
-// if (mediaSize != null) {
-// builder.setMediaSize(MediaSize.unflattenFromString(mediaSize));
-// }
-//
-// String resolution = parser.getAttributeValue(null, ATTRIBUTE_RESOLUTION);
-// if (resolution != null) {
-// builder.setMediaSize(Resolution.unflattenFromString(resolution));
-// }
-//
-// String margins = parser.getAttributeValue(null, ATTRIBUTE_MARGINS);
-// if (margins != null) {
-// builder.setMediaSize(Margins.unflattenFromString(margins));
-// }
-//
-// String inputTray = parser.getAttributeValue(null, ATTRIBUTE_INPUT_TRAY);
-// if (inputTray != null) {
-// builder.setMediaSize(Tray.unflattenFromString(inputTray));
-// }
-//
-// String outputTray = parser.getAttributeValue(null, ATTRIBUTE_OUTPUT_TRAY);
-// if (outputTray != null) {
-// builder.setMediaSize(Tray.unflattenFromString(outputTray));
-// }
-//
-// String duplexMode = parser.getAttributeValue(null, ATTRIBUTE_DUPLEX_MODE);
-// if (duplexMode != null) {
-// builder.setDuplexMode(Integer.parseInt(duplexMode));
-// }
-
- String colorMode = parser.getAttributeValue(null, ATTRIBUTE_COLOR_MODE);
- if (colorMode != null) {
- builder.setColorMode(Integer.parseInt(colorMode));
- }
-
- String fittingMode = parser.getAttributeValue(null, ATTRIBUTE_COLOR_MODE);
- if (fittingMode != null) {
- builder.setFittingMode(Integer.parseInt(fittingMode));
- }
- }
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
- parser.next();
PrintJobInfo printJob = new PrintJobInfo();
+
+ final int printJobId = Integer.parseInt(parser.getAttributeValue(null, ATTR_ID));
printJob.setId(printJobId);
- printJob.setTag(tag);
- printJob.setAppId(appId);
+ String label = parser.getAttributeValue(null, ATTR_LABEL);
printJob.setLabel(label);
+ final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE));
printJob.setState(state);
- printJob.setAttributes(attributes);
- printJob.setPrinterId(printerId);
+ final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID));
+ printJob.setAppId(appId);
+ final int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USER_ID));
+ printJob.setUserId(userId);
+ String tag = parser.getAttributeValue(null, ATTR_TAG);
+ printJob.setTag(tag);
+
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
+ String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
+ ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
+ null, ATTR_SERVICE));
+ printJob.setPrinterId(new PrinterId(service, localId));
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ List<PageRange> pageRanges = null;
+ while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) {
+ final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START));
+ final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END));
+ PageRange pageRange = new PageRange(start, end);
+ if (pageRanges == null) {
+ pageRanges = new ArrayList<PageRange>();
+ }
+ pageRanges.add(pageRange);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
+ parser.next();
+ }
+ if (pageRanges != null) {
+ printJob.setPages((PageRange[]) pageRanges.toArray());
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) {
+
+ PrintAttributes.Builder builder = new PrintAttributes.Builder();
+
+ String duplexMode = parser.getAttributeValue(null, ATTR_DUPLEX_MODE);
+ builder.setDuplexMode(Integer.parseInt(duplexMode));
+
+ String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
+ builder.setColorMode(Integer.parseInt(colorMode));
+
+ String fittingMode = parser.getAttributeValue(null, ATTR_FITTING_MODE);
+ builder.setFittingMode(Integer.parseInt(fittingMode));
+
+ String orientation = parser.getAttributeValue(null, ATTR_ORIENTATION);
+ builder.setOrientation(Integer.parseInt(orientation));
+
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ label = parser.getAttributeValue(null, ATTR_LABEL);
+ final int widthMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_WIDTH_MILS));
+ final int heightMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_HEIGHT_MILS));
+ MediaSize mediaSize = new MediaSize(id, label, widthMils, heightMils);
+ builder.setMediaSize(mediaSize);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ label = parser.getAttributeValue(null, ATTR_LABEL);
+ final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_HORIZONTAL_DPI));
+ final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_VERTICAL_DPI));
+ Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi);
+ builder.setResolution(resolution);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) {
+ final int leftMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_LEFT_MILS));
+ final int topMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_TOP_MILS));
+ final int rightMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_RIGHT_MILS));
+ final int bottomMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_BOTTOM_MILS));
+ Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils);
+ builder.setMargins(margins);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_MARGINS);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_INPUT_TRAY)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ label = parser.getAttributeValue(null, ATTR_LABEL);
+ Tray tray = new Tray(id, label);
+ builder.setInputTray(tray);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_INPUT_TRAY);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_OUTPUT_TRAY)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ label = parser.getAttributeValue(null, ATTR_LABEL);
+ Tray tray = new Tray(id, label);
+ builder.setOutputTray(tray);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_OUTPUT_TRAY);
+ parser.next();
+ }
+
+ printJob.setAttributes(builder.create());
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
+ final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_PAGE_COUNT));
+ final int contentType = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_CONTENT_TYPE));
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder().setPageCount(pageCount)
+ .setContentType(contentType).create();
+ printJob.setDocumentInfo(info);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO);
+ parser.next();
+ }
mPrintJobs.add(printJob);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 050332c..26d2a33 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -65,6 +65,8 @@
@Override
public IBinder onBind(Intent intent) {
+ mSpooler.restorePersistedState();
+
return new IPrintSpooler.Stub() {
@Override
public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
@@ -72,7 +74,7 @@
throws RemoteException {
List<PrintJobInfo> printJobs = null;
try {
- printJobs = mSpooler.getPrintJobs(componentName, state, appId);
+ printJobs = mSpooler.getPrintJobInfos(componentName, state, appId);
} finally {
callback.onGetPrintJobInfosResult(printJobs, sequence);
}
@@ -83,7 +85,7 @@
int appId, int sequence) throws RemoteException {
PrintJobInfo printJob = null;
try {
- printJob = mSpooler.getPrintJob(printJobId, appId);
+ printJob = mSpooler.getPrintJobInfo(printJobId, appId);
} finally {
callback.onGetPrintJobInfoResult(printJob, sequence);
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
index 912dd95..2bf62ee 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
@@ -17,6 +17,7 @@
package com.android.printspooler;
import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.ICancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -31,8 +32,6 @@
import android.util.Log;
import android.util.Slog;
-import libcore.io.IoUtils;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -42,6 +41,8 @@
import java.util.ArrayList;
import java.util.List;
+import libcore.io.IoUtils;
+
/**
* This class represents a remote print document adapter instance.
*/
@@ -55,6 +56,7 @@
public static final int STATE_LAYOUT_COMPLETED = 2;
public static final int STATE_WRITE_COMPLETED = 3;
public static final int STATE_FINISH_COMPLETED = 4;
+ public static final int STATE_FAILED = 6;
private final Object mLock = new Object();
@@ -76,22 +78,14 @@
Log.i(LOG_TAG, "getFile()");
}
synchronized (mLock) {
- if (mState < STATE_WRITE_COMPLETED) {
+ if (mState != STATE_WRITE_COMPLETED
+ && mState != STATE_FINISH_COMPLETED) {
throw new IllegalStateException("Write not completed");
}
return mFile;
}
}
- public void cancel() {
- synchronized (mLock) {
- final int taskCount = mTaskQueue.size();
- for (int i = 0; i < taskCount; i++) {
- mTaskQueue.remove(i).cancel();
- }
- }
- }
-
public void start() {
QueuedAsyncTask task = new QueuedAsyncTask() {
@Override
@@ -100,6 +94,7 @@
Log.i(LOG_TAG, "start()");
}
synchronized (mLock) {
+ mTaskQueue.add(this);
if (mState != STATE_INITIALIZED) {
throw new IllegalStateException("Invalid state: " + mState);
}
@@ -115,28 +110,22 @@
return null;
}
};
- synchronized (mLock) {
- mTaskQueue.add(task);
- task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
- }
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
}
public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
- LayoutResultCallback callback) {
- LayoutAsyncTask task = new LayoutAsyncTask(oldAttributes, newAttributes, callback);
- synchronized (mLock) {
- mTaskQueue.add(task);
- task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
- }
+ LayoutResultCallback callback, Bundle metadata) {
+ LayoutAsyncTask task = new LayoutAsyncTask(oldAttributes, newAttributes, callback,
+ metadata);
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
}
public void write(List<PageRange> pages, WriteResultCallback callback) {
WriteAsyncTask task = new WriteAsyncTask(pages, callback);
- mTaskQueue.add(task);
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
}
- public void finish() {
+ public void finish(final boolean abortPendingWork) {
QueuedAsyncTask task = new QueuedAsyncTask() {
@Override
protected Void doInBackground(Void... params) {
@@ -144,9 +133,15 @@
Log.i(LOG_TAG, "finish");
}
synchronized (mLock) {
- if (mState != STATE_LAYOUT_COMPLETED
- && mState != STATE_WRITE_COMPLETED) {
- throw new IllegalStateException("Invalid state: " + mState);
+ if (abortPendingWork) {
+ final int taskCount = mTaskQueue.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ mTaskQueue.remove(i).cancel();
+ }
+ }
+ mTaskQueue.add(this);
+ if (mState < STATE_START_COMPLETED) {
+ return null;
}
}
try {
@@ -156,15 +151,12 @@
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error reading file", re);
- mState = STATE_INITIALIZED;
+ mState = STATE_FAILED;
}
return null;
}
};
- synchronized (mLock) {
- mTaskQueue.add(task);
- task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
- }
+ task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
}
private abstract class QueuedAsyncTask extends AsyncTask<Void, Void, Void> {
@@ -181,6 +173,8 @@
private final LayoutResultCallback mCallback;
+ private final Bundle mMetadata;
+
private final ILayoutResultCallback mILayoutResultCallback =
new ILayoutResultCallback.Stub() {
@Override
@@ -219,11 +213,12 @@
private boolean mCompleted;
- public LayoutAsyncTask(PrintAttributes oldAttributes,
- PrintAttributes newAttributes, LayoutResultCallback callback) {
+ public LayoutAsyncTask(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ LayoutResultCallback callback, Bundle metadata) {
mOldAttributes = oldAttributes;
mNewAttributes = newAttributes;
mCallback = callback;
+ mMetadata = metadata;
}
@Override
@@ -238,6 +233,7 @@
@Override
protected Void doInBackground(Void... params) {
synchronized (mLock) {
+ mTaskQueue.add(this);
if (mState != STATE_START_COMPLETED
&& mState != STATE_LAYOUT_COMPLETED
&& mState != STATE_WRITE_COMPLETED) {
@@ -246,11 +242,10 @@
}
try {
mRemoteInterface.layout(mOldAttributes, mNewAttributes,
- mILayoutResultCallback);
+ mILayoutResultCallback, mMetadata);
synchronized (mLock) {
while (true) {
if (isCancelled()) {
- mState = STATE_INITIALIZED;
mTaskQueue.remove(this);
break;
}
@@ -268,7 +263,7 @@
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error calling layout", re);
- mState = STATE_INITIALIZED;
+ mState = STATE_FAILED;
}
return null;
}
@@ -357,7 +352,9 @@
Log.i(LOG_TAG, "print()");
}
synchronized (mLock) {
- if (mState != STATE_LAYOUT_COMPLETED) {
+ mTaskQueue.add(this);
+ if (mState != STATE_LAYOUT_COMPLETED
+ && mState != STATE_WRITE_COMPLETED) {
throw new IllegalStateException("Invalid state: " + mState);
}
}
@@ -398,7 +395,6 @@
synchronized (mLock) {
while (true) {
if (isCancelled()) {
- mState = STATE_INITIALIZED;
mTaskQueue.remove(this);
break;
}
@@ -416,10 +412,10 @@
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error writing print document", re);
- mState = STATE_INITIALIZED;
+ mState = STATE_FAILED;
} catch (IOException ioe) {
Slog.e(LOG_TAG, "Error writing print document", ioe);
- mState = STATE_INITIALIZED;
+ mState = STATE_FAILED;
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index dfc68f4..2629b11 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -64,6 +64,9 @@
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
<uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
+ <!-- Alarm clocks -->
+ <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
+
<application
android:persistent="true"
android:allowClearUserData="false"
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 5e18d0a..190c4be 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -63,13 +63,13 @@
<string name="compat_mode_off" msgid="4434467572461327898">"Rek uit v. schermvulling"</string>
<string name="compat_mode_help_header" msgid="7969493989397529910">"Compatibiliteitszoom"</string>
<string name="compat_mode_help_body" msgid="4946726776359270040">"Wanneer een app is ontworpen voor een kleiner scherm, wordt naast de klok een zoomknop weergegeven."</string>
- <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Schermafbeelding opslaan..."</string>
- <string name="screenshot_saving_title" msgid="8242282144535555697">"Schermafbeelding opslaan..."</string>
- <string name="screenshot_saving_text" msgid="2419718443411738818">"Schermafbeelding wordt opgeslagen."</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"Schermafbeelding gemaakt."</string>
- <string name="screenshot_saved_text" msgid="1152839647677558815">"Raak aan om uw schermafbeelding te bekijken."</string>
- <string name="screenshot_failed_title" msgid="705781116746922771">"Schermafbeelding is niet gemaakt."</string>
- <string name="screenshot_failed_text" msgid="8134011269572415402">"Kan schermafbeelding niet opslaan. Mogelijk is de opslag in gebruik."</string>
+ <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Screenshot opslaan..."</string>
+ <string name="screenshot_saving_title" msgid="8242282144535555697">"Screenshot opslaan..."</string>
+ <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot wordt opgeslagen."</string>
+ <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot gemaakt."</string>
+ <string name="screenshot_saved_text" msgid="1152839647677558815">"Raak aan om uw screenshot te bekijken."</string>
+ <string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot is niet gemaakt."</string>
+ <string name="screenshot_failed_text" msgid="8134011269572415402">"Kan screenshot niet opslaan. Mogelijk is de opslag in gebruik."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opties voor USB-bestandsoverdracht"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"Koppelen als mediaspeler (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"Koppelen als camera (PTP)"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index fa1e3fe..4fd8aee 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -109,7 +109,7 @@
private View mScrollView;
- private OnScaleGestureListener mScaleGestureListener
+ private OnScaleGestureListener mScaleGestureListener
= new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index ca6a06f..6fa863d 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -140,7 +140,7 @@
"\nvoid main(void) {\n" +
" gl_FragColor = texture2D(texture, outTexCoords);\n" +
"}\n\n";
-
+
private static final int FLOAT_SIZE_BYTES = 4;
private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
@@ -184,7 +184,7 @@
}
super.onCreate(surfaceHolder);
-
+
// TODO: Don't need this currently because the wallpaper service
// will restart the image wallpaper whenever the image changes.
//IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
@@ -512,44 +512,44 @@
private int loadTexture(Bitmap bitmap) {
int[] textures = new int[1];
-
+
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, textures, 0);
checkGlError();
-
+
int texture = textures[0];
glBindTexture(GL_TEXTURE_2D, texture);
checkGlError();
-
+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
+
GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
checkGlError();
return texture;
}
-
+
private int buildProgram(String vertex, String fragment) {
int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
if (vertexShader == 0) return 0;
-
+
int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
if (fragmentShader == 0) return 0;
-
+
int program = glCreateProgram();
glAttachShader(program, vertexShader);
checkGlError();
-
+
glAttachShader(program, fragmentShader);
checkGlError();
-
+
glLinkProgram(program);
checkGlError();
-
+
int[] status = new int[1];
glGetProgramiv(program, GL_LINK_STATUS, status, 0);
if (status[0] != GL_TRUE) {
@@ -560,19 +560,19 @@
glDeleteProgram(program);
return 0;
}
-
+
return program;
}
private int buildShader(String source, int type) {
int shader = glCreateShader(type);
-
+
glShaderSource(shader, source);
checkGlError();
-
+
glCompileShader(shader);
checkGlError();
-
+
int[] status = new int[1];
glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
if (status[0] != GL_TRUE) {
@@ -581,7 +581,7 @@
glDeleteShader(shader);
return 0;
}
-
+
return shader;
}
@@ -608,24 +608,24 @@
private boolean initGL(SurfaceHolder surfaceHolder) {
mEgl = (EGL10) EGLContext.getEGL();
-
+
mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed " +
GLUtils.getEGLErrorString(mEgl.eglGetError()));
}
-
+
int[] version = new int[2];
if (!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed " +
GLUtils.getEGLErrorString(mEgl.eglGetError()));
}
-
+
mEglConfig = chooseEglConfig();
if (mEglConfig == null) {
throw new RuntimeException("eglConfig not initialized");
}
-
+
mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
if (mEglContext == EGL_NO_CONTEXT) {
throw new RuntimeException("createContext failed " +
@@ -667,7 +667,7 @@
throw new RuntimeException("createWindowSurface failed " +
GLUtils.getEGLErrorString(error));
}
-
+
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
throw new RuntimeException("eglMakeCurrent failed " +
GLUtils.getEGLErrorString(mEgl.eglGetError()));
@@ -675,13 +675,13 @@
return true;
}
-
-
+
+
EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
- return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
+ return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
}
-
+
private EGLConfig chooseEglConfig() {
int[] configsCount = new int[1];
EGLConfig[] configs = new EGLConfig[1];
diff --git a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
index 37e974b..efbee5b 100644
--- a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
+++ b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
@@ -33,18 +33,18 @@
public class LoadAverageService extends Service {
private View mView;
-
+
private static final class Stats extends ProcessStats {
String mLoadText;
int mLoadWidth;
-
+
private final Paint mPaint;
-
+
Stats(Paint paint) {
super(false);
mPaint = paint;
}
-
+
@Override
public void onLoadChanged(float load1, float load5, float load15) {
mLoadText = load1 + " / " + load5 + " / " + load15;
@@ -56,7 +56,7 @@
return (int)mPaint.measureText(name);
}
}
-
+
private class LoadView extends View {
private Handler mHandler = new Handler() {
@Override
@@ -71,7 +71,7 @@
};
private final Stats mStats;
-
+
private Paint mLoadPaint;
private Paint mAddedPaint;
private Paint mRemovedPaint;
@@ -186,7 +186,7 @@
final int irqTime = stats.getLastIrqTime();
final int softIrqTime = stats.getLastSoftIrqTime();
final int idleTime = stats.getLastIdleTime();
-
+
final int totalTime = userTime+systemTime+iowaitTime+irqTime+softIrqTime+idleTime;
if (totalTime == 0) {
return;
@@ -269,7 +269,7 @@
maxWidth = st.nameWidth;
}
}
-
+
int neededWidth = mPaddingLeft + mPaddingRight + maxWidth;
int neededHeight = mPaddingTop + mPaddingBottom + (mFH*(1+NW));
if (neededWidth != mNeededWidth || neededHeight != mNeededHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index fec7329..cb624ad 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -28,7 +28,7 @@
public Map<Class<?>, Object> mComponents;
public abstract void start();
-
+
protected void onConfigurationChanged(Configuration newConfig) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index e084dac..a08eb9b 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -283,7 +283,7 @@
d.show();
mInvalidChargerDialog = d;
}
-
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print("mLowBatteryAlertCloseLevel=");
pw.println(mLowBatteryAlertCloseLevel);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 5617520..119299f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -222,12 +222,12 @@
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
Intent chooserIntent = Intent.createChooser(sharingIntent, null);
- chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
+ chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NEW_TASK);
mNotificationBuilder.addAction(R.drawable.ic_menu_share,
r.getString(com.android.internal.R.string.share),
- PendingIntent.getActivity(context, 0, chooserIntent,
+ PendingIntent.getActivity(context, 0, chooserIntent,
PendingIntent.FLAG_CANCEL_CURRENT));
OutputStream out = resolver.openOutputStream(uri);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 8b50a38..5bda813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -166,7 +166,7 @@
/**
* Returns the right icon to use for this item, respecting the iconId and
* iconPackage (if set)
- *
+ *
* @param context Context to use to get resources if iconPackage is not set
* @return Drawable for this item, or null if the package or item could not
* be found
@@ -193,7 +193,7 @@
if (icon.iconId == 0) {
return null;
}
-
+
try {
return r.getDrawable(icon.iconId);
} catch (RuntimeException e) {
@@ -285,7 +285,7 @@
}
public String toString() {
- return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon
+ return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon
+ " notification=" + mNotification + ")";
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 9eaee30..de33b87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -370,11 +370,11 @@
@Override
public void onFinishInflate() {
- mRotatedViews[Surface.ROTATION_0] =
+ mRotatedViews[Surface.ROTATION_0] =
mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
-
+
mRotatedViews[Surface.ROTATION_270] = NAVBAR_ALWAYS_AT_RIGHT
? findViewById(R.id.rot90)
: findViewById(R.id.rot270);
@@ -434,7 +434,7 @@
@Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
if (DEBUG) Log.d(TAG, String.format(
- "onLayout: %s (%d,%d,%d,%d)",
+ "onLayout: %s (%d,%d,%d,%d)",
changed?"changed":"notchanged", left, top, right, bottom));
super.onLayout(changed, left, top, right, bottom);
}
@@ -450,7 +450,7 @@
return super.onInterceptTouchEvent(ev);
}
*/
-
+
private String getResourceName(int resId) {
if (resId != 0) {
@@ -491,7 +491,7 @@
getWindowVisibleDisplayFrame(r);
final boolean offscreen = r.right > size.x || r.bottom > size.y;
- pw.println(" window: "
+ pw.println(" window: "
+ r.toShortString()
+ " " + visibilityToString(getWindowVisibility())
+ (offscreen ? " OFFSCREEN!" : ""));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 751d944..d55754b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -198,7 +198,7 @@
}
if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
if (!waiting && mState != STATE_CLOSED) {
- // it's possible that nothing animated, so we replicate the termination
+ // it's possible that nothing animated, so we replicate the termination
// conditions of panelExpansionChanged here
go(STATE_CLOSED);
onAllPanelsCollapsed();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index cf7f9c6..3a17c9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -226,7 +226,7 @@
return;
}
if (mPeekAnimator == null) {
- mPeekAnimator = ObjectAnimator.ofFloat(this,
+ mPeekAnimator = ObjectAnimator.ofFloat(this,
"expandedHeight", mPeekHeight)
.setDuration(250);
}
@@ -339,7 +339,7 @@
mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
- mPeekHeight = res.getDimension(R.dimen.peek_height)
+ mPeekHeight = res.getDimension(R.dimen.peek_height)
+ getPaddingBottom() // our window might have a dropshadow
- (mHandleView == null ? 0 : mHandleView.getPaddingTop()); // the handle might have a topshadow
}
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 fd99f5b..6eadd2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -147,7 +147,7 @@
private float mExpandAccelPx; // classic value: 2000px/s/s
private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
- private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
+ private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
// faster than mSelfCollapseVelocityPx)
PhoneStatusBarPolicy mIconPolicy;
@@ -173,11 +173,11 @@
// viewgroup containing the normal contents of the statusbar
LinearLayout mStatusBarContents;
-
+
// right-hand icons
LinearLayout mSystemIconArea;
-
- // left-hand icons
+
+ // left-hand icons
LinearLayout mStatusIcons;
// the icons themselves
IconMerger mNotificationIcons;
@@ -204,7 +204,7 @@
// top bar
View mNotificationPanelHeader;
- View mDateTimeView;
+ View mDateTimeView;
View mClearButton;
ImageView mSettingsButton, mNotificationButton;
@@ -264,7 +264,7 @@
// XXX: gesture research
private final GestureRecorder mGestureRec = DEBUG_GESTURES
- ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
+ ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
: null;
private int mNavigationIconHints = 0;
@@ -391,7 +391,7 @@
mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
mStatusBarView.setBar(this);
-
+
PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
mStatusBarView.setPanelHolder(holder);
@@ -629,7 +629,7 @@
}
}
- mClingShown = ! (DEBUG_CLINGS
+ mClingShown = ! (DEBUG_CLINGS
|| !Prefs.read(mContext).getBoolean(Prefs.SHOWN_QUICK_SETTINGS_HELP, false));
if (!ENABLE_NOTIFICATION_PANEL_CLING || ActivityManager.isRunningInTestHarness()) {
@@ -1080,7 +1080,7 @@
protected void updateCarrierLabelVisibility(boolean force) {
if (!mShowCarrierInPanel) return;
- // The idea here is to only show the carrier label when there is enough room to see it,
+ // The idea here is to only show the carrier label when there is enough room to see it,
// i.e. when there aren't enough notifications to fill the panel.
if (DEBUG) {
Log.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
@@ -1092,7 +1092,7 @@
!(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
&& mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight)
&& mScrollView.getVisibility() == View.VISIBLE;
-
+
if (force || mCarrierLabelVisible != makeVisible) {
mCarrierLabelVisible = makeVisible;
if (DEBUG) {
@@ -1131,8 +1131,8 @@
+ " any=" + any + " clearable=" + clearable);
}
- if (mHasFlipSettings
- && mFlipSettingsView != null
+ if (mHasFlipSettings
+ && mFlipSettingsView != null
&& mFlipSettingsView.getVisibility() == View.VISIBLE
&& mScrollView.getVisibility() != View.VISIBLE) {
// the flip settings panel is unequivocally showing; we should not be shown
@@ -1444,7 +1444,7 @@
a.setStartDelay(d);
return a;
}
-
+
public Animator start(Animator a) {
a.start();
return a;
@@ -1573,7 +1573,7 @@
interpolator(mAccelerateInterpolator,
ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f)
)
- .setDuration(FLIP_DURATION_OUT),
+ .setDuration(FLIP_DURATION_OUT),
mScrollView, View.INVISIBLE));
mSettingsButtonAnim = start(
setVisibilityWhenDone(
@@ -1674,13 +1674,13 @@
/**
* Enables or disables layers on the children of the notifications pile.
- *
+ *
* When layers are enabled, this method attempts to enable layers for the minimal
* number of children. Only children visible when the notification area is fully
* expanded will receive a layer. The technique used in this method might cause
* more children than necessary to get a layer (at most one extra child with the
* current UI.)
- *
+ *
* @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE}
*/
private void setPileLayers(int layerType) {
@@ -1693,7 +1693,7 @@
}
break;
case View.LAYER_TYPE_HARDWARE:
- final int[] location = new int[2];
+ final int[] location = new int[2];
mNotificationPanel.getLocationInWindow(location);
final int left = location[0];
@@ -1803,9 +1803,9 @@
// Cling (first-run help) handling.
// The cling is supposed to show the first time you drag, or even tap, the status bar.
- // It should show the notification panel, then fade in after half a second, giving you
+ // It should show the notification panel, then fade in after half a second, giving you
// an explanation of what just happened, as well as teach you how to access quick
- // settings (another drag). The user can dismiss the cling by clicking OK or by
+ // settings (another drag). The user can dismiss the cling by clicking OK or by
// dragging quick settings into view.
final int act = event.getActionMasked();
if (mSuppressStatusBarDrags) {
@@ -2284,7 +2284,7 @@
void updateDisplaySize() {
mDisplay.getMetrics(mDisplayMetrics);
if (DEBUG_GESTURES) {
- mGestureRec.tag("display",
+ mGestureRec.tag("display",
String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 2a65381..d2e7bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -114,9 +114,9 @@
if (mFullWidthNotifications) {
// No double swiping. If either panel is open, nothing else can be pulled down.
- return ((mSettingsPanel == null ? 0 : mSettingsPanel.getExpandedHeight())
- + mNotificationPanel.getExpandedHeight() > 0)
- ? null
+ return ((mSettingsPanel == null ? 0 : mSettingsPanel.getExpandedHeight())
+ + mNotificationPanel.getExpandedHeight() > 0)
+ ? null
: mNotificationPanel;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 9ee6065..a9c5c79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -44,6 +44,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.AlarmClock;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Profile;
@@ -605,12 +606,7 @@
alarmTile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- // TODO: Jump into the alarm application
- Intent intent = new Intent();
- intent.setComponent(new ComponentName(
- "com.google.android.deskclock",
- "com.android.deskclock.AlarmClock"));
- startSettingsActivity(intent);
+ startSettingsActivity(AlarmClock.ACTION_SET_ALARM);
}
});
mModel.addAlarmTile(alarmTile, new QuickSettingsModel.RefreshCallback() {
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 a784f37..a6ce288 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
@@ -38,7 +38,7 @@
public abstract class Ticker {
private static final int TICKER_SEGMENT_DELAY = 3000;
-
+
private Context mContext;
private Handler mHandler = new Handler();
private ArrayList<Segment> mSegments = new ArrayList();
@@ -216,15 +216,15 @@
if (initialCount == 0 && mSegments.size() > 0) {
Segment seg = mSegments.get(0);
seg.first = false;
-
+
mIconSwitcher.setAnimateFirstView(false);
mIconSwitcher.reset();
mIconSwitcher.setImageDrawable(seg.icon);
-
+
mTextSwitcher.setAnimateFirstView(false);
mTextSwitcher.reset();
mTextSwitcher.setText(seg.getText());
-
+
tickerStarting();
scheduleAdvance();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 452ff489..575b44e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -71,7 +71,7 @@
boolean plugged = false;
switch (status) {
- case BatteryManager.BATTERY_STATUS_CHARGING:
+ case BatteryManager.BATTERY_STATUS_CHARGING:
case BatteryManager.BATTERY_STATUS_FULL:
plugged = true;
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index e5816cd..93fb14f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -192,7 +192,7 @@
return formatted;
}
}
-
+
return result;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index 27a3a15..847f997 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -63,7 +63,7 @@
mAttachedToWindow = true;
setUpdates();
}
-
+
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index dbf9957..f6ac4a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -40,10 +40,10 @@
Rect mTmpRect = new Rect();
private SwipeHelper mSwipeHelper;
-
+
BaseStatusBar mBar;
private ViewGroup mContentHolder;
-
+
private Notification mHeadsUp;
private OnClickListener mOnClickListener;
@@ -62,14 +62,14 @@
float densityScale = getResources().getDisplayMetrics().density;
float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
-
+
mContentHolder = (ViewGroup) findViewById(R.id.contentHolder);
if (mHeadsUp != null) {
// whoops, we're on already!
applyContent(mHeadsUp, mOnClickListener);
}
}
-
+
public void setBar(BaseStatusBar bar) {
mBar = bar;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index fc77be1..f325957 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -81,7 +81,7 @@
defStyle, 0);
mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
-
+
mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
@@ -90,7 +90,7 @@
mGlowWidth = mGlowBG.getIntrinsicWidth();
mGlowHeight = mGlowBG.getIntrinsicHeight();
}
-
+
a.recycle();
setClickable(true);
@@ -180,7 +180,7 @@
}
final AnimatorSet as = mPressedAnim = new AnimatorSet();
if (pressed) {
- if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
+ if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
mGlowScale = GLOW_MAX_SCALE_FACTOR;
if (mGlowAlpha < BUTTON_QUIESCENT_ALPHA)
mGlowAlpha = BUTTON_QUIESCENT_ALPHA;
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 63f1254..c5563da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -88,7 +88,7 @@
textResId = R.string.gps_notification_searching_text;
visible = true;
}
-
+
try {
if (visible) {
Intent gpsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
@@ -108,14 +108,14 @@
// Notification.Builder will helpfully fill these out for you no matter what you do
n.tickerView = null;
n.tickerText = null;
-
+
n.priority = Notification.PRIORITY_HIGH;
int[] idOut = new int[1];
mNotificationService.enqueueNotificationWithTag(
mContext.getPackageName(), mContext.getBasePackageName(),
- null,
- GPS_NOTIFICATION_ID,
+ null,
+ GPS_NOTIFICATION_ID,
n,
idOut,
UserHandle.USER_ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index cd8445c..92c57c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -36,8 +36,8 @@
import java.util.HashMap;
-public class NotificationRowLayout
- extends LinearLayout
+public class NotificationRowLayout
+ extends LinearLayout
implements SwipeHelper.Callback, ExpandHelper.Callback
{
private static final String TAG = "NotificationRowLayout";
@@ -55,7 +55,7 @@
HashMap<View, ValueAnimator> mDisappearingViews = new HashMap<View, ValueAnimator>();
private SwipeHelper mSwipeHelper;
-
+
private OnSizeChangedListener mOnSizeChangedListener;
// Flag set during notification removal animation to avoid causing too much work until
@@ -74,7 +74,7 @@
mRealLayoutTransition = new LayoutTransition();
mRealLayoutTransition.setAnimateParentHierarchy(true);
setLayoutTransitionsEnabled(true);
-
+
setOrientation(LinearLayout.VERTICAL);
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index b75f8b3..2c36ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -41,7 +41,7 @@
/**
* The notification that is shown when a USB mass storage host
- * is connected.
+ * is connected.
* <p>
* This is lazily created, so use {@link #setUsbStorageNotification()}.
*/
@@ -217,7 +217,7 @@
setMediaStorageNotification(
com.android.internal.R.string.ext_media_unmountable_notification_title,
com.android.internal.R.string.ext_media_unmountable_notification_message,
- com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
+ com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
updateUsbMassStorageNotification(mUmsAvailable);
} else if (newState.equals(Environment.MEDIA_REMOVED)) {
/*
@@ -283,7 +283,7 @@
if (notificationManager == null) {
return;
}
-
+
if (visible) {
Resources r = Resources.getSystem();
CharSequence title = r.getText(titleId);
@@ -300,7 +300,7 @@
} else {
mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
}
-
+
mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
mUsbStorageNotification.tickerText = title;
@@ -329,7 +329,7 @@
mUsbStorageNotification.fullScreenIntent = pi;
}
}
-
+
final int notificationId = mUsbStorageNotification.icon;
if (visible) {
notificationManager.notifyAsUser(null, notificationId, mUsbStorageNotification,
@@ -373,7 +373,7 @@
final int notificationId = mMediaStorageNotification.icon;
notificationManager.cancel(notificationId);
}
-
+
if (visible) {
Resources r = Resources.getSystem();
CharSequence title = r.getText(titleId);
@@ -402,7 +402,7 @@
mMediaStorageNotification.icon = icon;
mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
}
-
+
final int notificationId = mMediaStorageNotification.icon;
if (visible) {
notificationManager.notifyAsUser(null, notificationId,
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
index 65315f3..0ed2a54 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
@@ -94,7 +94,7 @@
switchDisplay(on);
}
};
-
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -105,7 +105,7 @@
Log.w(TAG, "Failed to get StorageManager");
}
}
-
+
mUIHandler = new Handler();
HandlerThread thr = new HandlerThread("SystemUI UsbStorageActivity");
@@ -184,7 +184,7 @@
@Override
protected void onPause() {
super.onPause();
-
+
unregisterReceiver(mUsbStateReceiver);
if (mStorageManager == null && mStorageListener != null) {
mStorageManager.unregisterListener(mStorageListener);
@@ -256,7 +256,7 @@
// will be hidden once USB mass storage kicks in (or fails)
}
});
-
+
// things to do elsewhere
mAsyncStorageHandler.post(new Runnable() {
@Override
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 2460a91..bd56ee0 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1045,10 +1045,8 @@
// SystemUI (status bar) layout policy
int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
- if (shortSizeDp < 600) {
- // Allow the navigation bar to move on small devices (phones).
- mNavigationBarCanMove = true;
- }
+ // Allow the navigation bar to move on small devices (phones).
+ mNavigationBarCanMove = shortSizeDp < 600;
mHasNavigationBar = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_showNavigationBar);
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 5b64d8a..07731fc 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -2631,6 +2631,7 @@
info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat,
y.fuzz, y.resolution);
}
+ info->setButtonUnderPad(mParameters.hasButtonUnderPad);
}
}
@@ -2796,6 +2797,9 @@
mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
}
+ mParameters.hasButtonUnderPad=
+ getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD);
+
String8 deviceTypeString;
if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
deviceTypeString)) {
@@ -4650,6 +4654,14 @@
mCurrentFingerIdBits, positions);
}
+ // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
+ // to NEUTRAL, then we should not generate tap event.
+ if (mPointerGesture.lastGestureMode != PointerGesture::HOVER
+ && mPointerGesture.lastGestureMode != PointerGesture::TAP
+ && mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
+ mPointerGesture.resetTap();
+ }
+
// Pick a new active touch id if needed.
// Choose an arbitrary pointer that just went down, if there is one.
// Otherwise choose an arbitrary remaining pointer.
@@ -4858,8 +4870,12 @@
}
} else {
#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP, %0.3fms since down",
- (when - mPointerGesture.tapDownTime) * 0.000001f);
+ if (mPointerGesture.tapDownTime != LLONG_MIN) {
+ ALOGD("Gestures: Not a TAP, %0.3fms since down",
+ (when - mPointerGesture.tapDownTime) * 0.000001f);
+ } else {
+ ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+ }
#endif
}
}
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 7e303e4..3ed426f 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -1205,6 +1205,7 @@
bool hasAssociatedDisplay;
bool associatedDisplayIsExternal;
bool orientationAware;
+ bool hasButtonUnderPad;
enum GestureMode {
GESTURE_MODE_POINTER,
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 0be9ca5..d7cdb9d 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -38,7 +38,6 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.text.TextUtils;
-import android.text.format.Time;
import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
@@ -57,31 +56,38 @@
import java.util.Map;
import java.util.TimeZone;
+import static android.app.AlarmManager.RTC_WAKEUP;
+import static android.app.AlarmManager.RTC;
+import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.app.AlarmManager.ELAPSED_REALTIME;
+
import com.android.internal.util.LocalLog;
class AlarmManagerService extends IAlarmManager.Stub {
// The threshold for how long an alarm can be late before we print a
// warning message. The time duration is in milliseconds.
private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
-
- private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP;
- private static final int RTC_MASK = 1 << AlarmManager.RTC;
- private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP;
- private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
+
+ private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
+ private static final int RTC_MASK = 1 << RTC;
+ private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
+ private static final int ELAPSED_REALTIME_MASK = 1 << ELAPSED_REALTIME;
private static final int TIME_CHANGED_MASK = 1 << 16;
private static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK;
- // Alignment quantum for inexact repeating alarms
- private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
+ // Mask for testing whether a given alarm type is wakeup vs non-wakeup
+ private static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup
private static final String TAG = "AlarmManager";
private static final String ClockReceiver_TAG = "ClockReceiver";
private static final boolean localLOGV = false;
+ private static final boolean DEBUG_BATCH = localLOGV || false;
private static final int ALARM_EVENT = 1;
private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
private static final Intent mBackgroundIntent
= new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
+ private static final IncreasingTimeOrder sIncreasingTimeOrder = new IncreasingTimeOrder();
private static final boolean WAKEUP_STATS = true;
@@ -90,14 +96,10 @@
private final LocalLog mLog = new LocalLog(TAG);
private Object mLock = new Object();
-
- private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
- private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
- private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
- private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
- private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
-
+
private int mDescriptor;
+ private long mNextWakeup;
+ private long mNextNonWakeup;
private int mBroadcastRefCount = 0;
private PowerManager.WakeLock mWakeLock;
private ArrayList<InFlight> mInFlight = new ArrayList<InFlight>();
@@ -124,6 +126,277 @@
private final LinkedList<WakeupEvent> mRecentWakeups = new LinkedList<WakeupEvent>();
private final long RECENT_WAKEUP_PERIOD = 1000L * 60 * 60 * 24; // one day
+ static final class Batch {
+ long start; // These endpoints are always in ELAPSED
+ long end;
+ boolean standalone; // certain "batches" don't participate in coalescing
+
+ final ArrayList<Alarm> alarms = new ArrayList<Alarm>();
+
+ Batch() {
+ start = 0;
+ end = Long.MAX_VALUE;
+ }
+
+ Batch(Alarm seed) {
+ start = seed.whenElapsed;
+ end = seed.maxWhen;
+ alarms.add(seed);
+ }
+
+ int size() {
+ return alarms.size();
+ }
+
+ Alarm get(int index) {
+ return alarms.get(index);
+ }
+
+ boolean canHold(long whenElapsed, long maxWhen) {
+ return (end >= whenElapsed) && (start <= maxWhen);
+ }
+
+ boolean add(Alarm alarm) {
+ boolean newStart = false;
+ // narrows the batch if necessary; presumes that canHold(alarm) is true
+ int index = Collections.binarySearch(alarms, alarm, sIncreasingTimeOrder);
+ if (index < 0) {
+ index = 0 - index - 1;
+ }
+ alarms.add(index, alarm);
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "Adding " + alarm + " to " + this);
+ }
+ if (alarm.whenElapsed > start) {
+ start = alarm.whenElapsed;
+ newStart = true;
+ }
+ if (alarm.maxWhen < end) {
+ end = alarm.maxWhen;
+ }
+
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, " => now " + this);
+ }
+ return newStart;
+ }
+
+ boolean remove(final PendingIntent operation) {
+ boolean didRemove = false;
+ long newStart = 0; // recalculate endpoints as we go
+ long newEnd = Long.MAX_VALUE;
+ for (int i = 0; i < alarms.size(); i++) {
+ Alarm alarm = alarms.get(i);
+ if (alarm.operation.equals(operation)) {
+ alarms.remove(i);
+ didRemove = true;
+ } else {
+ if (alarm.whenElapsed > newStart) {
+ newStart = alarm.whenElapsed;
+ }
+ if (alarm.maxWhen < newEnd) {
+ newEnd = alarm.maxWhen;
+ }
+ i++;
+ }
+ }
+ if (didRemove) {
+ // commit the new batch bounds
+ start = newStart;
+ end = newEnd;
+ }
+ return didRemove;
+ }
+
+ boolean remove(final String packageName) {
+ boolean didRemove = false;
+ long newStart = 0; // recalculate endpoints as we go
+ long newEnd = Long.MAX_VALUE;
+ for (int i = 0; i < alarms.size(); i++) {
+ Alarm alarm = alarms.get(i);
+ if (alarm.operation.getTargetPackage().equals(packageName)) {
+ alarms.remove(i);
+ didRemove = true;
+ } else {
+ if (alarm.whenElapsed > newStart) {
+ newStart = alarm.whenElapsed;
+ }
+ if (alarm.maxWhen < newEnd) {
+ newEnd = alarm.maxWhen;
+ }
+ i++;
+ }
+ }
+ if (didRemove) {
+ // commit the new batch bounds
+ start = newStart;
+ end = newEnd;
+ }
+ return didRemove;
+ }
+
+ boolean remove(final int userHandle) {
+ boolean didRemove = false;
+ long newStart = 0; // recalculate endpoints as we go
+ long newEnd = Long.MAX_VALUE;
+ for (int i = 0; i < alarms.size(); i++) {
+ Alarm alarm = alarms.get(i);
+ if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) {
+ alarms.remove(i);
+ didRemove = true;
+ } else {
+ if (alarm.whenElapsed > newStart) {
+ newStart = alarm.whenElapsed;
+ }
+ if (alarm.maxWhen < newEnd) {
+ newEnd = alarm.maxWhen;
+ }
+ i++;
+ }
+ }
+ if (didRemove) {
+ // commit the new batch bounds
+ start = newStart;
+ end = newEnd;
+ }
+ return didRemove;
+ }
+
+ boolean hasPackage(final String packageName) {
+ final int N = alarms.size();
+ for (int i = 0; i < N; i++) {
+ Alarm a = alarms.get(i);
+ if (a.operation.getTargetPackage().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean hasWakeups() {
+ final int N = alarms.size();
+ for (int i = 0; i < N; i++) {
+ Alarm a = alarms.get(i);
+ // non-wakeup alarms are types 1 and 3, i.e. have the low bit set
+ if ((a.type & TYPE_NONWAKEUP_MASK) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder(40);
+ b.append("Batch{"); b.append(Integer.toHexString(this.hashCode()));
+ b.append(" num="); b.append(size());
+ b.append(" start="); b.append(start);
+ b.append(" end="); b.append(end);
+ if (standalone) {
+ b.append(" STANDALONE");
+ }
+ b.append('}');
+ return b.toString();
+ }
+ }
+
+ static class BatchTimeOrder implements Comparator<Batch> {
+ public int compare(Batch b1, Batch b2) {
+ long when1 = b1.start;
+ long when2 = b2.start;
+ if (when1 - when2 > 0) {
+ return 1;
+ }
+ if (when1 - when2 < 0) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ // minimum recurrence period or alarm futurity for us to be able to fuzz it
+ private static final long MIN_FUZZABLE_INTERVAL = 10000;
+ private static final BatchTimeOrder sBatchOrder = new BatchTimeOrder();
+ private final ArrayList<Batch> mAlarmBatches = new ArrayList<Batch>();
+
+ static long convertToElapsed(long when, int type) {
+ final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
+ if (isRtc) {
+ when -= System.currentTimeMillis() - SystemClock.elapsedRealtime();
+ }
+ return when;
+ }
+
+ // Apply a heuristic to { recurrence interval, futurity of the trigger time } to
+ // calculate the end of our nominal delivery window for the alarm.
+ static long maxTriggerTime(long now, long triggerAtTime, long interval) {
+ // Current heuristic: batchable window is 75% of either the recurrence interval
+ // [for a periodic alarm] or of the time from now to the desired delivery time,
+ // with a minimum delay/interval of 10 seconds, under which we will simply not
+ // defer the alarm.
+ long futurity = (interval == 0)
+ ? (triggerAtTime - now)
+ : interval;
+ if (futurity < MIN_FUZZABLE_INTERVAL) {
+ futurity = 0;
+ }
+ return triggerAtTime + (long)(.75 * futurity);
+ }
+
+ // returns true if the batch was added at the head
+ static boolean addBatchLocked(ArrayList<Batch> list, Batch newBatch) {
+ int index = Collections.binarySearch(list, newBatch, sBatchOrder);
+ if (index < 0) {
+ index = 0 - index - 1;
+ }
+ list.add(index, newBatch);
+ return (index == 0);
+ }
+
+ Batch attemptCoalesceLocked(long whenElapsed, long maxWhen) {
+ final int N = mAlarmBatches.size();
+ for (int i = 0; i < N; i++) {
+ Batch b = mAlarmBatches.get(i);
+ if (!b.standalone && b.canHold(whenElapsed, maxWhen)) {
+ return b;
+ }
+ }
+ return null;
+ }
+
+ // The RTC clock has moved arbitrarily, so we need to recalculate all the batching
+ void rebatchAllAlarms() {
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "RTC changed; rebatching");
+ }
+ synchronized (mLock) {
+ ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone();
+ mAlarmBatches.clear();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ for (Batch batch : oldSet) {
+ final int N = batch.size();
+ for (int i = 0; i < N; i++) {
+ Alarm a = batch.get(i);
+ long whenElapsed = convertToElapsed(a.when, a.type);
+ long maxElapsed = (a.whenElapsed == a.maxWhen)
+ ? whenElapsed
+ : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
+ if (batch.standalone) {
+ // this will also be the only alarm in the batch
+ a = new Alarm(a.type, a.when, whenElapsed, maxElapsed,
+ a.repeatInterval, a.operation);
+ Batch newBatch = new Batch(a);
+ newBatch.standalone = true;
+ addBatchLocked(mAlarmBatches, newBatch);
+ } else {
+ setImplLocked(a.type, a.when, whenElapsed, maxElapsed,
+ a.repeatInterval, a.operation, false);
+ }
+ }
+ }
+ }
+ }
+
private static final class InFlight extends Intent {
final PendingIntent mPendingIntent;
final Pair<String, ComponentName> mTarget;
@@ -184,6 +457,7 @@
public AlarmManagerService(Context context) {
mContext = context;
mDescriptor = init();
+ mNextWakeup = mNextNonWakeup = 0;
// We have to set current TimeZone info to kernel
// because kernel doesn't keep this after reboot
@@ -224,77 +498,102 @@
super.finalize();
}
}
-
- public void set(int type, long triggerAtTime, PendingIntent operation) {
- setRepeating(type, triggerAtTime, 0, operation);
- }
-
- public void setRepeating(int type, long triggerAtTime, long interval,
+
+ @Override
+ public void set(int type, long triggerAtTime, long windowLength, long interval,
PendingIntent operation) {
+ set(type, triggerAtTime, windowLength, interval, operation, false);
+ }
+
+ public void set(int type, long triggerAtTime, long windowLength, long interval,
+ PendingIntent operation, boolean isStandalone) {
if (operation == null) {
Slog.w(TAG, "set/setRepeating ignored because there is no intent");
return;
}
+
+ // Sanity check the window length. This will catch people mistakenly
+ // trying to pass an end-of-window timestamp rather than a duration.
+ if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
+ Slog.w(TAG, "Window length " + windowLength
+ + "ms suspiciously long; limiting to 1 hour");
+ windowLength = AlarmManager.INTERVAL_HOUR;
+ }
+
+ if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) {
+ throw new IllegalArgumentException("Invalid alarm type " + type);
+ }
+
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long triggerElapsed = convertToElapsed(triggerAtTime, type);
+ final long maxElapsed;
+ if (windowLength == AlarmManager.WINDOW_EXACT) {
+ maxElapsed = triggerElapsed;
+ } else if (windowLength < 0) {
+ maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
+ } else {
+ maxElapsed = triggerElapsed + windowLength;
+ }
+
synchronized (mLock) {
- Alarm alarm = new Alarm();
- alarm.type = type;
- alarm.when = triggerAtTime;
- alarm.repeatInterval = interval;
- alarm.operation = operation;
-
- // Remove this alarm if already scheduled.
- removeLocked(operation);
-
- if (localLOGV) Slog.v(TAG, "set: " + alarm);
-
- int index = addAlarmLocked(alarm);
- if (index == 0) {
- setLocked(alarm);
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "set(" + operation + ") : type=" + type
+ + " triggerAtTime=" + triggerAtTime + " win=" + windowLength
+ + " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed
+ + " interval=" + interval + " standalone=" + isStandalone);
}
+ setImplLocked(type, triggerAtTime, triggerElapsed, maxElapsed,
+ interval, operation, isStandalone);
}
}
-
- public void setInexactRepeating(int type, long triggerAtTime, long interval,
- PendingIntent operation) {
- if (operation == null) {
- Slog.w(TAG, "setInexactRepeating ignored because there is no intent");
- return;
- }
- if (interval <= 0) {
- Slog.w(TAG, "setInexactRepeating ignored because interval " + interval
- + " is invalid");
- return;
- }
+ private void setImplLocked(int type, long when, long whenElapsed, long maxWhen, long interval,
+ PendingIntent operation, boolean isStandalone) {
+ Alarm a = new Alarm(type, when, whenElapsed, maxWhen, interval, operation);
+ removeLocked(operation);
- // If the requested interval isn't a multiple of 15 minutes, just treat it as exact
- if (interval % QUANTUM != 0) {
- if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple");
- setRepeating(type, triggerAtTime, interval, operation);
- return;
- }
-
- // Translate times into the ELAPSED timebase for alignment purposes so that
- // alignment never tries to match against wall clock times.
- final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP);
- final long skew = (isRtc)
- ? System.currentTimeMillis() - SystemClock.elapsedRealtime()
- : 0;
-
- // Slip forward to the next ELAPSED-timebase quantum after the stated time. If
- // we're *at* a quantum point, leave it alone.
- final long adjustedTriggerTime;
- long offset = (triggerAtTime - skew) % QUANTUM;
- if (offset != 0) {
- adjustedTriggerTime = triggerAtTime - offset + QUANTUM;
+ final boolean reschedule;
+ Batch batch = (isStandalone) ? null : attemptCoalesceLocked(whenElapsed, maxWhen);
+ if (batch == null) {
+ batch = new Batch(a);
+ batch.standalone = isStandalone;
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "Starting new alarm batch " + batch);
+ }
+ reschedule = addBatchLocked(mAlarmBatches, batch);
} else {
- adjustedTriggerTime = triggerAtTime;
+ reschedule = batch.add(a);
}
- // Set the alarm based on the quantum-aligned start time
- if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval
- + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime);
- setRepeating(type, adjustedTriggerTime, interval, operation);
+ if (reschedule) {
+ rescheduleKernelAlarmsLocked();
+ }
+ }
+
+ private Batch findFirstWakeupBatchLocked() {
+ final int N = mAlarmBatches.size();
+ for (int i = 0; i < N; i++) {
+ Batch b = mAlarmBatches.get(i);
+ if (b.hasWakeups()) {
+ return b;
+ }
+ }
+ return null;
+ }
+
+ private void rescheduleKernelAlarmsLocked() {
+ // Schedule the next upcoming wakeup alarm. If there is a deliverable batch
+ // prior to that which contains no wakeups, we schedule that as well.
+ final Batch firstWakeup = findFirstWakeupBatchLocked();
+ final Batch firstBatch = mAlarmBatches.get(0);
+ if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
+ mNextWakeup = firstWakeup.start;
+ setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
+ }
+ if (firstBatch != firstWakeup && mNextNonWakeup != firstBatch.start) {
+ mNextNonWakeup = firstBatch.start;
+ setLocked(ELAPSED_REALTIME, firstBatch.start);
+ }
}
public void setTime(long millis) {
@@ -356,160 +655,61 @@
}
public void removeLocked(PendingIntent operation) {
- removeLocked(mRtcWakeupAlarms, operation);
- removeLocked(mRtcAlarms, operation);
- removeLocked(mElapsedRealtimeWakeupAlarms, operation);
- removeLocked(mElapsedRealtimeAlarms, operation);
- }
-
- private void removeLocked(ArrayList<Alarm> alarmList,
- PendingIntent operation) {
- if (alarmList.size() <= 0) {
- return;
- }
-
- // iterator over the list removing any it where the intent match
- for (int i=0; i<alarmList.size(); i++) {
- Alarm alarm = alarmList.get(i);
- if (alarm.operation.equals(operation)) {
- alarmList.remove(i);
- i--;
+ for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+ Batch b = mAlarmBatches.get(i);
+ b.remove(operation);
+ if (b.size() == 0) {
+ mAlarmBatches.remove(i);
}
}
}
public void removeLocked(String packageName) {
- removeLocked(mRtcWakeupAlarms, packageName);
- removeLocked(mRtcAlarms, packageName);
- removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
- removeLocked(mElapsedRealtimeAlarms, packageName);
- }
-
- private void removeLocked(ArrayList<Alarm> alarmList,
- String packageName) {
- if (alarmList.size() <= 0) {
- return;
- }
-
- // iterator over the list removing any it where the intent match
- for (int i=0; i<alarmList.size(); i++) {
- Alarm alarm = alarmList.get(i);
- if (alarm.operation.getTargetPackage().equals(packageName)) {
- alarmList.remove(i);
- i--;
+ for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+ Batch b = mAlarmBatches.get(i);
+ b.remove(packageName);
+ if (b.size() == 0) {
+ mAlarmBatches.remove(i);
}
}
}
public void removeUserLocked(int userHandle) {
- removeUserLocked(mRtcWakeupAlarms, userHandle);
- removeUserLocked(mRtcAlarms, userHandle);
- removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle);
- removeUserLocked(mElapsedRealtimeAlarms, userHandle);
- }
-
- private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) {
- if (alarmList.size() <= 0) {
- return;
- }
-
- // iterator over the list removing any it where the intent match
- for (int i=0; i<alarmList.size(); i++) {
- Alarm alarm = alarmList.get(i);
- if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) {
- alarmList.remove(i);
- i--;
+ for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+ Batch b = mAlarmBatches.get(i);
+ b.remove(userHandle);
+ if (b.size() == 0) {
+ mAlarmBatches.remove(i);
}
}
}
-
- public boolean lookForPackageLocked(String packageName) {
- return lookForPackageLocked(mRtcWakeupAlarms, packageName)
- || lookForPackageLocked(mRtcAlarms, packageName)
- || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
- || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
- }
- private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
- for (int i=alarmList.size()-1; i>=0; i--) {
- if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
+ public boolean lookForPackageLocked(String packageName) {
+ for (int i = 0; i < mAlarmBatches.size(); i++) {
+ Batch b = mAlarmBatches.get(i);
+ if (b.hasPackage(packageName)) {
return true;
}
}
return false;
}
-
- private ArrayList<Alarm> getAlarmList(int type) {
- switch (type) {
- case AlarmManager.RTC_WAKEUP: return mRtcWakeupAlarms;
- case AlarmManager.RTC: return mRtcAlarms;
- case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
- case AlarmManager.ELAPSED_REALTIME: return mElapsedRealtimeAlarms;
- }
-
- return null;
- }
-
- private int addAlarmLocked(Alarm alarm) {
- ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
-
- int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
- if (index < 0) {
- index = 0 - index - 1;
- }
- if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
- alarmList.add(index, alarm);
- if (localLOGV) {
- // Display the list of alarms for this alarm type
- Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
- int position = 0;
- for (Alarm a : alarmList) {
- Time time = new Time();
- time.set(a.when);
- String timeStr = time.format("%b %d %I:%M:%S %p");
- Slog.v(TAG, position + ": " + timeStr
- + " " + a.operation.getTargetPackage());
- position += 1;
- }
- }
-
- return index;
- }
-
- public long timeToNextAlarm() {
- long nextAlarm = Long.MAX_VALUE;
- synchronized (mLock) {
- for (int i=AlarmManager.RTC_WAKEUP;
- i<=AlarmManager.ELAPSED_REALTIME; i++) {
- ArrayList<Alarm> alarmList = getAlarmList(i);
- if (alarmList.size() > 0) {
- Alarm a = alarmList.get(0);
- if (a.when < nextAlarm) {
- nextAlarm = a.when;
- }
- }
- }
- }
- return nextAlarm;
- }
-
- private void setLocked(Alarm alarm)
+ private void setLocked(int type, long when)
{
if (mDescriptor != -1)
{
// The kernel never triggers alarms with negative wakeup times
// so we ensure they are positive.
long alarmSeconds, alarmNanoseconds;
- if (alarm.when < 0) {
+ if (when < 0) {
alarmSeconds = 0;
alarmNanoseconds = 0;
} else {
- alarmSeconds = alarm.when / 1000;
- alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
+ alarmSeconds = when / 1000;
+ alarmNanoseconds = (when % 1000) * 1000 * 1000;
}
- set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
+ set(mDescriptor, type, alarmSeconds, alarmNanoseconds);
}
else
{
@@ -517,7 +717,7 @@
msg.what = ALARM_EVENT;
mHandler.removeMessages(ALARM_EVENT);
- mHandler.sendMessageAtTime(msg, alarm.when);
+ mHandler.sendMessageAtTime(msg, when);
}
}
@@ -533,29 +733,28 @@
synchronized (mLock) {
pw.println("Current Alarm Manager state:");
- if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
- final long now = System.currentTimeMillis();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- pw.println(" ");
- pw.print(" Realtime wakeup (now=");
- pw.print(sdf.format(new Date(now))); pw.println("):");
- if (mRtcWakeupAlarms.size() > 0) {
- dumpAlarmList(pw, mRtcWakeupAlarms, " ", "RTC_WAKEUP", now);
- }
- if (mRtcAlarms.size() > 0) {
- dumpAlarmList(pw, mRtcAlarms, " ", "RTC", now);
- }
- }
- if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
- final long now = SystemClock.elapsedRealtime();
- pw.println(" ");
- pw.print(" Elapsed realtime wakeup (now=");
- TimeUtils.formatDuration(now, pw); pw.println("):");
- if (mElapsedRealtimeWakeupAlarms.size() > 0) {
- dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, " ", "ELAPSED_WAKEUP", now);
- }
- if (mElapsedRealtimeAlarms.size() > 0) {
- dumpAlarmList(pw, mElapsedRealtimeAlarms, " ", "ELAPSED", now);
+ final long nowRTC = System.currentTimeMillis();
+ final long nowELAPSED = SystemClock.elapsedRealtime();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ pw.print("nowRTC="); pw.print(nowRTC);
+ pw.print("="); pw.print(sdf.format(new Date(nowRTC)));
+ pw.print(" nowELAPSED="); pw.println(nowELAPSED);
+
+ long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
+ long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED);
+ pw.print("Next alarm: "); pw.print(mNextNonWakeup);
+ pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
+ pw.print("Next wakeup: "); pw.print(mNextWakeup);
+ pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
+
+ if (mAlarmBatches.size() > 0) {
+ pw.println();
+ pw.print("Pending alarm batches: ");
+ pw.println(mAlarmBatches.size());
+ for (Batch b : mAlarmBatches) {
+ pw.print(b); pw.println(':');
+ dumpAlarmList(pw, b.alarms, " ", nowELAPSED, nowRTC);
}
}
@@ -662,7 +861,6 @@
if (WAKEUP_STATS) {
pw.println();
pw.println(" Recent Wakeup History:");
- final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss.SSS");
long last = -1;
for (WakeupEvent event : mRecentWakeups) {
pw.print(" "); pw.print(sdf.format(new Date(event.when)));
@@ -691,77 +889,78 @@
a.dump(pw, prefix + " ", now);
}
}
-
+
+ private static final String labelForType(int type) {
+ switch (type) {
+ case RTC: return "RTC";
+ case RTC_WAKEUP : return "RTC_WAKEUP";
+ case ELAPSED_REALTIME : return "ELAPSED";
+ case ELAPSED_REALTIME_WAKEUP: return "ELAPSED_WAKEUP";
+ default:
+ break;
+ }
+ return "--unknown--";
+ }
+
+ private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
+ String prefix, long nowELAPSED, long nowRTC) {
+ for (int i=list.size()-1; i>=0; i--) {
+ Alarm a = list.get(i);
+ final String label = labelForType(a.type);
+ long now = (a.type <= RTC) ? nowRTC : nowELAPSED;
+ pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
+ pw.print(": "); pw.println(a);
+ a.dump(pw, prefix + " ", now);
+ }
+ }
+
private native int init();
private native void close(int fd);
private native void set(int fd, int type, long seconds, long nanoseconds);
private native int waitForAlarm(int fd);
private native int setKernelTimezone(int fd, int minuteswest);
- private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
- ArrayList<Alarm> triggerList,
- long now)
- {
- ArrayList<Alarm> repeats = null;
+ private void triggerAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED, long nowRTC) {
+ Batch batch;
- for (int i=0; i<alarmList.size(); i++) {
- Alarm alarm = alarmList.get(i);
-
- if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
-
- if (alarm.when > now) {
- // don't fire alarms in the future
+ // batches are temporally sorted, so we need only pull from the
+ // start of the list until we either empty it or hit a batch
+ // that is not yet deliverable
+ while ((batch = mAlarmBatches.get(0)) != null) {
+ if (batch.start > nowELAPSED) {
+ // Everything else is scheduled for the future
break;
}
-
- // If the alarm is late, then print a warning message.
- // Note that this can happen if the user creates a new event on
- // the Calendar app with a reminder that is in the past. In that
- // case, the reminder alarm will fire immediately.
- if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
- Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
- + " now: " + now + " delay (in seconds): "
- + (now - alarm.when) / 1000);
- }
- // Recurring alarms may have passed several alarm intervals while the
- // phone was asleep or off, so pass a trigger count when sending them.
- if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
- alarm.count = 1;
- if (alarm.repeatInterval > 0) {
- // this adjustment will be zero if we're late by
- // less than one full repeat interval
- alarm.count += (now - alarm.when) / alarm.repeatInterval;
- }
- triggerList.add(alarm);
-
- // remove the alarm from the list
- alarmList.remove(i);
- i--;
+ // We will (re)schedule some alarms now; don't let that interfere
+ // with delivery of this current batch
+ mAlarmBatches.remove(0);
- // if it repeats queue it up to be read-added to the list
- if (alarm.repeatInterval > 0) {
- if (repeats == null) {
- repeats = new ArrayList<Alarm>();
+ final int N = batch.size();
+ for (int i = 0; i < N; i++) {
+ Alarm alarm = batch.get(i);
+ alarm.count = 1;
+ triggerList.add(alarm);
+
+ // Recurring alarms may have passed several alarm intervals while the
+ // phone was asleep or off, so pass a trigger count when sending them.
+ if (alarm.repeatInterval > 0) {
+ // this adjustment will be zero if we're late by
+ // less than one full repeat interval
+ alarm.count += (nowELAPSED - alarm.whenElapsed) / alarm.repeatInterval;
+
+ // Also schedule its next recurrence
+ final long delta = alarm.count * alarm.repeatInterval;
+ final long nextElapsed = alarm.whenElapsed + delta;
+ setImplLocked(alarm.type, alarm.when + delta, nextElapsed,
+ maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
+ alarm.repeatInterval, alarm.operation, batch.standalone);
}
- repeats.add(alarm);
- }
- }
- // reset any repeating alarms.
- if (repeats != null) {
- for (int i=0; i<repeats.size(); i++) {
- Alarm alarm = repeats.get(i);
- alarm.when += alarm.count * alarm.repeatInterval;
- addAlarmLocked(alarm);
}
}
-
- if (alarmList.size() > 0) {
- setLocked(alarmList.get(0));
- }
}
-
+
/**
* This Comparator sorts Alarms into increasing time order.
*/
@@ -783,15 +982,21 @@
public int type;
public int count;
public long when;
+ public long whenElapsed; // 'when' in the elapsed time base
+ public long maxWhen; // also in the elapsed time base
public long repeatInterval;
public PendingIntent operation;
- public Alarm() {
- when = 0;
- repeatInterval = 0;
- operation = null;
+ public Alarm(int _type, long _when, long _whenElapsed, long _maxWhen,
+ long _interval, PendingIntent _op) {
+ type = _type;
+ when = _when;
+ whenElapsed = _whenElapsed;
+ maxWhen = _maxWhen;
+ repeatInterval = _interval;
+ operation = _op;
}
-
+
@Override
public String toString()
{
@@ -808,6 +1013,7 @@
public void dump(PrintWriter pw, String prefix, long now) {
pw.print(prefix); pw.print("type="); pw.print(type);
+ pw.print(" whenElapsed="); pw.print(whenElapsed);
pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
pw.print(" repeatInterval="); pw.print(repeatInterval);
pw.print(" count="); pw.println(count);
@@ -815,16 +1021,22 @@
}
}
- void recordWakeupAlarms(ArrayList<Alarm> alarms, long now, long skewToRTC) {
- for (Alarm a : alarms) {
- if (a.when > now) {
+ void recordWakeupAlarms(ArrayList<Batch> batches, long nowELAPSED, long nowRTC) {
+ final int numBatches = batches.size();
+ for (int nextBatch = 0; nextBatch < numBatches; nextBatch++) {
+ Batch b = batches.get(nextBatch);
+ if (b.start > nowELAPSED) {
break;
}
- WakeupEvent e = new WakeupEvent(now + skewToRTC,
- a.operation.getCreatorUid(),
- a.operation.getIntent().getAction());
- mRecentWakeups.add(e);
+ final int numAlarms = b.alarms.size();
+ for (int nextAlarm = 0; nextAlarm < numAlarms; nextAlarm++) {
+ Alarm a = b.alarms.get(nextAlarm);
+ WakeupEvent e = new WakeupEvent(nowRTC,
+ a.operation.getCreatorUid(),
+ a.operation.getIntent().getAction());
+ mRecentWakeups.add(e);
+ }
}
}
@@ -847,6 +1059,7 @@
if ((result & TIME_CHANGED_MASK) != 0) {
remove(mTimeTickSender);
+ rebatchAllAlarms();
mClockReceiver.scheduleTimeTickEvent();
Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -873,28 +1086,14 @@
mRecentWakeups.remove();
}
- recordWakeupAlarms(mRtcWakeupAlarms,
- nowRTC,
- 0);
- recordWakeupAlarms(mElapsedRealtimeWakeupAlarms,
- nowELAPSED,
- nowRTC - nowELAPSED);
+ recordWakeupAlarms(mAlarmBatches, nowELAPSED, nowRTC);
}
}
- if ((result & RTC_WAKEUP_MASK) != 0)
- triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
-
- if ((result & RTC_MASK) != 0)
- triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
-
- if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
- triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
-
- if ((result & ELAPSED_REALTIME_MASK) != 0)
- triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
-
- // now trigger the alarms
+ triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
+ rescheduleKernelAlarmsLocked();
+
+ // now deliver the alarm intents
for (int i=0; i<triggerList.size(); i++) {
Alarm alarm = triggerList.get(i);
try {
@@ -930,8 +1129,8 @@
} else {
fs.nesting++;
}
- if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
- || alarm.type == AlarmManager.RTC_WAKEUP) {
+ if (alarm.type == ELAPSED_REALTIME_WAKEUP
+ || alarm.type == RTC_WAKEUP) {
bs.numWakeup++;
fs.numWakeup++;
ActivityManagerNative.noteWakeupAlarm(
@@ -980,10 +1179,8 @@
ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
synchronized (mLock) {
final long nowRTC = System.currentTimeMillis();
- triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
- triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
- triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
- triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
+ final long nowELAPSED = SystemClock.elapsedRealtime();
+ triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
}
// now trigger the alarms without the lock held
@@ -1035,8 +1232,8 @@
// the top of the next minute.
final long tickEventDelay = nextTime - currentTime;
- set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay,
- mTimeTickSender);
+ set(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,
+ 0, mTimeTickSender, true);
}
public void scheduleDateChangedEvent() {
@@ -1048,7 +1245,7 @@
calendar.set(Calendar.MILLISECOND, 0);
calendar.add(Calendar.DAY_OF_MONTH, 1);
- set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
+ set(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, true);
}
}
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 7a107e7..6b4d248 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -40,6 +40,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -97,6 +98,8 @@
}
public final static class Op {
+ public final int uid;
+ public final String packageName;
public final int op;
public int mode;
public int duration;
@@ -104,7 +107,9 @@
public long rejectTime;
public int nesting;
- public Op(int _op) {
+ public Op(int _uid, String _packageName, int _op) {
+ uid = _uid;
+ packageName = _packageName;
op = _op;
mode = AppOpsManager.MODE_ALLOWED;
}
@@ -112,10 +117,10 @@
final SparseArray<ArrayList<Callback>> mOpModeWatchers
= new SparseArray<ArrayList<Callback>>();
- final HashMap<String, ArrayList<Callback>> mPackageModeWatchers
- = new HashMap<String, ArrayList<Callback>>();
- final HashMap<IBinder, Callback> mModeWatchers
- = new HashMap<IBinder, Callback>();
+ final ArrayMap<String, ArrayList<Callback>> mPackageModeWatchers
+ = new ArrayMap<String, ArrayList<Callback>>();
+ final ArrayMap<IBinder, Callback> mModeWatchers
+ = new ArrayMap<IBinder, Callback>();
public final class Callback implements DeathRecipient {
final IAppOpsCallback mCallback;
@@ -138,6 +143,47 @@
}
}
+ final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
+
+ public final class ClientState extends Binder implements DeathRecipient {
+ final IBinder mAppToken;
+ final int mPid;
+ final ArrayList<Op> mStartedOps;
+
+ public ClientState(IBinder appToken) {
+ mAppToken = appToken;
+ mPid = Binder.getCallingPid();
+ if (appToken instanceof Binder) {
+ // For local clients, there is no reason to track them.
+ mStartedOps = null;
+ } else {
+ mStartedOps = new ArrayList<Op>();
+ try {
+ mAppToken.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClientState{" +
+ "mAppToken=" + mAppToken +
+ ", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
+ '}';
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (AppOpsService.this) {
+ for (int i=mStartedOps.size()-1; i>=0; i--) {
+ finishOperationLocked(mStartedOps.get(i));
+ }
+ mClients.remove(mAppToken);
+ }
+ }
+ }
+
public AppOpsService(File storagePath) {
mFile = new AtomicFile(storagePath);
mHandler = new Handler();
@@ -380,21 +426,18 @@
Callback cb = mModeWatchers.remove(callback.asBinder());
if (cb != null) {
cb.unlinkToDeath();
- for (int i=0; i<mOpModeWatchers.size(); i++) {
+ for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
cbs.remove(cb);
if (cbs.size() <= 0) {
mOpModeWatchers.removeAt(i);
}
}
- if (mPackageModeWatchers.size() > 0) {
- Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator();
- while (it.hasNext()) {
- ArrayList<Callback> cbs = it.next();
- cbs.remove(cb);
- if (cbs.size() <= 0) {
- it.remove();
- }
+ for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
+ ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i);
+ cbs.remove(cb);
+ if (cbs.size() <= 0) {
+ mPackageModeWatchers.removeAt(i);
}
}
}
@@ -402,6 +445,18 @@
}
@Override
+ public IBinder getToken(IBinder clientToken) {
+ synchronized (this) {
+ ClientState cs = mClients.get(clientToken);
+ if (cs == null) {
+ cs = new ClientState(clientToken);
+ mClients.put(clientToken, cs);
+ }
+ return cs;
+ }
+ }
+
+ @Override
public int checkOperation(int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
@@ -448,9 +503,10 @@
}
@Override
- public int startOperation(int code, int uid, String packageName) {
+ public int startOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ ClientState client = (ClientState)token;
synchronized (this) {
Ops ops = getOpsLocked(uid, packageName, true);
if (ops == null) {
@@ -475,32 +531,46 @@
op.duration = -1;
}
op.nesting++;
+ if (client.mStartedOps != null) {
+ client.mStartedOps.add(op);
+ }
return AppOpsManager.MODE_ALLOWED;
}
}
@Override
- public void finishOperation(int code, int uid, String packageName) {
+ public void finishOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ ClientState client = (ClientState)token;
synchronized (this) {
Op op = getOpLocked(code, uid, packageName, true);
if (op == null) {
return;
}
- if (op.nesting <= 1) {
- if (op.nesting == 1) {
- op.duration = (int)(System.currentTimeMillis() - op.time);
- op.time += op.duration;
- } else {
- Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
- + " code " + code + " time=" + op.time + " duration=" + op.duration
- + " nesting=" + op.nesting);
+ if (client.mStartedOps != null) {
+ if (!client.mStartedOps.remove(op)) {
+ throw new IllegalStateException("Operation not started: uid" + op.uid
+ + " pkg=" + op.packageName + " op=" + op.op);
}
- op.nesting = 0;
- } else {
- op.nesting--;
}
+ finishOperationLocked(op);
+ }
+ }
+
+ void finishOperationLocked(Op op) {
+ if (op.nesting <= 1) {
+ if (op.nesting == 1) {
+ op.duration = (int)(System.currentTimeMillis() - op.time);
+ op.time += op.duration;
+ } else {
+ Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
+ + op.packageName + " code " + op.op + " time=" + op.time
+ + " duration=" + op.duration + " nesting=" + op.nesting);
+ }
+ op.nesting = 0;
+ } else {
+ op.nesting--;
}
}
@@ -601,7 +671,7 @@
if (!edit) {
return null;
}
- op = new Op(code);
+ op = new Op(ops.uid, ops.packageName, code);
ops.put(code, op);
}
if (edit) {
@@ -711,7 +781,7 @@
String tagName = parser.getName();
if (tagName.equals("op")) {
- Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
+ Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
String mode = parser.getAttributeValue(null, "m");
if (mode != null) {
op.mode = Integer.parseInt(mode);
@@ -831,6 +901,62 @@
synchronized (this) {
pw.println("Current AppOps Service state:");
final long now = System.currentTimeMillis();
+ boolean needSep = false;
+ if (mOpModeWatchers.size() > 0) {
+ needSep = true;
+ pw.println(" Op mode watchers:");
+ for (int i=0; i<mOpModeWatchers.size(); i++) {
+ pw.print(" Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
+ pw.println(":");
+ ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i);
+ for (int j=0; j<callbacks.size(); j++) {
+ pw.print(" #"); pw.print(j); pw.print(": ");
+ pw.println(callbacks.get(j));
+ }
+ }
+ }
+ if (mPackageModeWatchers.size() > 0) {
+ needSep = true;
+ pw.println(" Package mode watchers:");
+ for (int i=0; i<mPackageModeWatchers.size(); i++) {
+ pw.print(" Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
+ pw.println(":");
+ ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i);
+ for (int j=0; j<callbacks.size(); j++) {
+ pw.print(" #"); pw.print(j); pw.print(": ");
+ pw.println(callbacks.get(j));
+ }
+ }
+ }
+ if (mModeWatchers.size() > 0) {
+ needSep = true;
+ pw.println(" All mode watchers:");
+ for (int i=0; i<mModeWatchers.size(); i++) {
+ pw.print(" "); pw.print(mModeWatchers.keyAt(i));
+ pw.print(" -> "); pw.println(mModeWatchers.valueAt(i));
+ }
+ }
+ if (mClients.size() > 0) {
+ needSep = true;
+ pw.println(" Clients:");
+ for (int i=0; i<mClients.size(); i++) {
+ pw.print(" "); pw.print(mClients.keyAt(i)); pw.println(":");
+ ClientState cs = mClients.valueAt(i);
+ pw.print(" "); pw.println(cs);
+ if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
+ pw.println(" Started ops:");
+ for (int j=0; j<cs.mStartedOps.size(); j++) {
+ Op op = cs.mStartedOps.get(j);
+ pw.print(" "); pw.print("uid="); pw.print(op.uid);
+ pw.print(" pkg="); pw.print(op.packageName);
+ pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
+ }
+ }
+ }
+ }
+ if (needSep) {
+ pw.println();
+ }
for (int i=0; i<mUidOps.size(); i++) {
pw.print(" Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 476a6fd..0608b6a 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -42,6 +42,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.CaptivePortalTracker;
@@ -96,8 +97,9 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
-import android.util.SparseIntArray;
import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.Xml;
import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
@@ -107,6 +109,7 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.Nat464Xlat;
@@ -121,7 +124,13 @@
import dalvik.system.DexClassLoader;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
@@ -170,7 +179,6 @@
private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
private Tethering mTethering;
- private boolean mTetheringConfigValid = false;
private KeyStore mKeyStore;
@@ -381,6 +389,9 @@
TelephonyManager mTelephonyManager;
+ // We only want one checkMobileProvisioning after booting.
+ volatile boolean mFirstProvisioningCheckStarted = false;
+
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
// Currently, omitting a NetworkFactory will create one internally
@@ -583,12 +594,8 @@
}
mTethering = new Tethering(mContext, mNetd, statsService, this, mHandler.getLooper());
- mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
- mTethering.getTetherableWifiRegexs().length != 0 ||
- mTethering.getTetherableBluetoothRegexs().length != 0) &&
- mTethering.getUpstreamIfaceTypes().length != 0);
- //set up the listener for user state for creating user VPNs
+ //set up the listener for user state for creating user VPNs
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTING);
intentFilter.addAction(Intent.ACTION_USER_STOPPING);
@@ -2755,6 +2762,17 @@
state + "/" + info.getDetailedState());
}
+ // After booting we'll check once for mobile provisioning
+ // if we've provisioned by and connected.
+ if (!mFirstProvisioningCheckStarted
+ && (0 != Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0))
+ && (state == NetworkInfo.State.CONNECTED)) {
+ log("check provisioning after booting");
+ mFirstProvisioningCheckStarted = true;
+ checkMobileProvisioning(true, CheckMp.MAX_TIMEOUT_MS, null);
+ }
+
EventLogTags.writeConnectivityStateChanged(
info.getType(), info.getSubtype(), info.getDetailedState().ordinal());
@@ -3004,7 +3022,10 @@
int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
boolean tetherEnabledInSettings = (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.TETHER_SUPPORTED, defaultVal) != 0);
- return tetherEnabledInSettings && mTetheringConfigValid;
+ return tetherEnabledInSettings && ((mTethering.getTetherableUsbRegexs().length != 0 ||
+ mTethering.getTetherableWifiRegexs().length != 0 ||
+ mTethering.getTetherableBluetoothRegexs().length != 0) &&
+ mTethering.getUpstreamIfaceTypes().length != 0);
}
// An API NetworkStateTrackers can call when they lose their network.
@@ -3670,13 +3691,15 @@
}
@Override
- public int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs,
+ public int checkMobileProvisioning(final boolean sendNotification, int suggestedTimeOutMs,
final ResultReceiver resultReceiver) {
log("checkMobileProvisioning: E sendNotification=" + sendNotification
+ " suggestedTimeOutMs=" + suggestedTimeOutMs
+ " resultReceiver=" + resultReceiver);
enforceChangePermission();
+ mFirstProvisioningCheckStarted = true;
+
int timeOutMs = suggestedTimeOutMs;
if (suggestedTimeOutMs > CheckMp.MAX_TIMEOUT_MS) {
timeOutMs = CheckMp.MAX_TIMEOUT_MS;
@@ -3703,6 +3726,10 @@
log("CheckMp.onComplete: send result");
resultReceiver.send(result, null);
}
+ if (!sendNotification) {
+ log("CheckMp.onComplete: done, not sending notification");
+ return;
+ }
NetworkInfo ni =
mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI].getNetworkInfo();
switch(result) {
@@ -3713,10 +3740,9 @@
}
case ConnectivityManager.CMP_RESULT_CODE_REDIRECTED: {
log("CheckMp.onComplete: warm sim");
- String url = getProvisioningUrl();
+ String url = getMobileProvisioningUrl();
if (TextUtils.isEmpty(url)) {
- url = mContext.getResources()
- .getString(R.string.mobile_redirected_provisioning_url);
+ url = getMobileRedirectedProvisioningUrl();
}
if (TextUtils.isEmpty(url) == false) {
log("CheckMp.onComplete: warm sim (redirected), url=" + url);
@@ -3728,7 +3754,7 @@
}
case ConnectivityManager.CMP_RESULT_CODE_NO_DNS:
case ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION: {
- String url = getProvisioningUrl();
+ String url = getMobileProvisioningUrl();
if (TextUtils.isEmpty(url) == false) {
log("CheckMp.onComplete: warm sim (no dns/tcp), url=" + url);
setNotificationVisible(true, ni, url);
@@ -4102,10 +4128,114 @@
log("setNotificationVisible: X visible=" + visible + " ni=" + networkInfo + " url=" + url);
}
- private String getProvisioningUrl() {
- String url = mContext.getResources().getString(R.string.mobile_provisioning_url);
- log("getProvisioningUrl: mobile_provisioning_url=" + url);
+ /** Location to an updatable file listing carrier provisioning urls.
+ * An example:
+ *
+ * <?xml version="1.0" encoding="utf-8"?>
+ * <provisioningUrls>
+ * <provisioningUrl mcc="310" mnc="4">http://myserver.com/foo?mdn=%3$s&iccid=%1$s&imei=%2$s</provisioningUrl>
+ * <redirectedUrl mcc="310" mnc="4">http://www.google.com</redirectedUrl>
+ * </provisioningUrls>
+ */
+ private static final String PROVISIONING_URL_PATH =
+ "/data/misc/radio/provisioning_urls.xml";
+ private final File mProvisioningUrlFile = new File(PROVISIONING_URL_PATH);
+ /** XML tag for root element. */
+ private static final String TAG_PROVISIONING_URLS = "provisioningUrls";
+ /** XML tag for individual url */
+ private static final String TAG_PROVISIONING_URL = "provisioningUrl";
+ /** XML tag for redirected url */
+ private static final String TAG_REDIRECTED_URL = "redirectedUrl";
+ /** XML attribute for mcc */
+ private static final String ATTR_MCC = "mcc";
+ /** XML attribute for mnc */
+ private static final String ATTR_MNC = "mnc";
+
+ private static final int REDIRECTED_PROVISIONING = 1;
+ private static final int PROVISIONING = 2;
+
+ private String getProvisioningUrlBaseFromFile(int type) {
+ FileReader fileReader = null;
+ XmlPullParser parser = null;
+ Configuration config = mContext.getResources().getConfiguration();
+ String tagType;
+
+ switch (type) {
+ case PROVISIONING:
+ tagType = TAG_PROVISIONING_URL;
+ break;
+ case REDIRECTED_PROVISIONING:
+ tagType = TAG_REDIRECTED_URL;
+ break;
+ default:
+ throw new RuntimeException("getProvisioningUrlBaseFromFile: Unexpected parameter " +
+ type);
+ }
+
+ try {
+ fileReader = new FileReader(mProvisioningUrlFile);
+ parser = Xml.newPullParser();
+ parser.setInput(fileReader);
+ XmlUtils.beginDocument(parser, TAG_PROVISIONING_URLS);
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+
+ String element = parser.getName();
+ if (element == null) break;
+
+ if (element.equals(tagType)) {
+ String mcc = parser.getAttributeValue(null, ATTR_MCC);
+ try {
+ if (mcc != null && Integer.parseInt(mcc) == config.mcc) {
+ String mnc = parser.getAttributeValue(null, ATTR_MNC);
+ if (mnc != null && Integer.parseInt(mnc) == config.mnc) {
+ parser.next();
+ if (parser.getEventType() == XmlPullParser.TEXT) {
+ return parser.getText();
+ }
+ }
+ }
+ } catch (NumberFormatException e) {
+ loge("NumberFormatException in getProvisioningUrlBaseFromFile: " + e);
+ }
+ }
+ }
+ return null;
+ } catch (FileNotFoundException e) {
+ loge("Carrier Provisioning Urls file not found");
+ } catch (XmlPullParserException e) {
+ loge("Xml parser exception reading Carrier Provisioning Urls file: " + e);
+ } catch (IOException e) {
+ loge("I/O exception reading Carrier Provisioning Urls file: " + e);
+ } finally {
+ if (fileReader != null) {
+ try {
+ fileReader.close();
+ } catch (IOException e) {}
+ }
+ }
+ return null;
+ }
+
+ private String getMobileRedirectedProvisioningUrl() {
+ String url = getProvisioningUrlBaseFromFile(REDIRECTED_PROVISIONING);
+ if (TextUtils.isEmpty(url)) {
+ url = mContext.getResources().getString(R.string.mobile_redirected_provisioning_url);
+ }
+ return url;
+ }
+
+ public String getMobileProvisioningUrl() {
+ enforceConnectivityInternalPermission();
+ String url = getProvisioningUrlBaseFromFile(PROVISIONING);
+ if (TextUtils.isEmpty(url)) {
+ url = mContext.getResources().getString(R.string.mobile_provisioning_url);
+ log("getProvisioningUrl: mobile_provisioining_url from resource =" + url);
+ } else {
+ log("getProvisioningUrl: mobile_provisioning_url from File =" + url);
+ }
// populate the iccid, imei and phone number in the provisioning url.
if (!TextUtils.isEmpty(url)) {
String phoneNumber = mTelephonyManager.getLine1Number();
diff --git a/services/java/com/android/server/TwilightService.java b/services/java/com/android/server/TwilightService.java
index 154de1c..0356faa 100644
--- a/services/java/com/android/server/TwilightService.java
+++ b/services/java/com/android/server/TwilightService.java
@@ -520,7 +520,7 @@
Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
mAlarmManager.cancel(pendingIntent);
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
+ mAlarmManager.setExact(AlarmManager.RTC, nextUpdate, pendingIntent);
}
};
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index 9b5f8f6..28eb948 100644
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -342,7 +342,8 @@
// Lock held on mVibrations
private void startVibrationLocked(final Vibration vib) {
try {
- int mode = mAppOpsService.startOperation(AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
+ int mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
+ AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
if (mode == AppOpsManager.MODE_ERRORED) {
Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
@@ -366,7 +367,8 @@
private void reportFinishVibrationLocked() {
if (mCurrentVibration != null) {
try {
- mAppOpsService.finishOperation(AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
+ mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
+ AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
mCurrentVibration.mPackageName);
} catch (RemoteException e) {
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b51415f..ef84254 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.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.ResolverActivity;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessStats;
@@ -479,38 +480,31 @@
int pid;
IBinder token;
}
- final SparseArray<ForegroundToken> mForegroundProcesses
- = new SparseArray<ForegroundToken>();
+ final SparseArray<ForegroundToken> mForegroundProcesses = new SparseArray<ForegroundToken>();
/**
* List of records for processes that someone had tried to start before the
* system was ready. We don't start them at that point, but ensure they
* are started by the time booting is complete.
*/
- final ArrayList<ProcessRecord> mProcessesOnHold
- = new ArrayList<ProcessRecord>();
+ final ArrayList<ProcessRecord> mProcessesOnHold = new ArrayList<ProcessRecord>();
/**
* List of persistent applications that are in the process
* of being started.
*/
- final ArrayList<ProcessRecord> mPersistentStartingProcesses
- = new ArrayList<ProcessRecord>();
+ final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
/**
* Processes that are being forcibly torn down.
*/
- final ArrayList<ProcessRecord> mRemovedProcesses
- = new ArrayList<ProcessRecord>();
+ final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
/**
* List of running applications, sorted by recent usage.
* The first entry in the list is the least recently used.
- * It contains ApplicationRecord objects. This list does NOT include
- * any persistent application records (since we never want to exit them).
*/
- final ArrayList<ProcessRecord> mLruProcesses
- = new ArrayList<ProcessRecord>();
+ final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
/**
* List of processes that should gc as soon as things are idle.
@@ -2056,7 +2050,7 @@
final void setFocusedActivityLocked(ActivityRecord r) {
if (mFocusedActivity != r) {
- if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivitiyLocked: r=" + r);
+ if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivityLocked: r=" + r);
mFocusedActivity = r;
mStackSupervisor.setFocusedStack(r);
if (r != null) {
@@ -2493,6 +2487,20 @@
}
}
+ String getHomePackageName() {
+ Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
+ intent.setComponent(mTopComponent);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, mCurrentUserId);
+ if (aInfo != null) {
+ final String homePackageName = aInfo.applicationInfo.packageName;
+ if (!ResolverActivity.class.getName().equals(homePackageName)) {
+ return homePackageName;
+ }
+ }
+ return null;
+ }
+
boolean startHomeActivityLocked(int userId) {
if (mHeadless) {
// Added because none of the other calls to ensureBootCompleted seem to fire
@@ -13754,6 +13762,12 @@
int clientAdj = computeOomAdjLocked(client, cachedAdj,
TOP_APP, doingAll, now);
int clientProcState = client.curProcState;
+ if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+ // If the other app is cached for any reason, for purposes here
+ // we are going to consider it empty. The specific cached state
+ // doesn't propagate except under certain conditions.
+ clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ }
String adjType = null;
if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
// Not doing bind OOM management, so treat
@@ -13801,8 +13815,8 @@
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
app.adjType = "cch-client-act";
}
+ app.hasClientActivities = true;
}
- app.hasClientActivities |= client.hasActivities;
}
}
if (adj > clientAdj) {
@@ -13912,6 +13926,12 @@
continue;
}
int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
+ int clientProcState = client.curProcState;
+ if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+ // If the other app is cached for any reason, for purposes here
+ // we are going to consider it empty.
+ clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ }
if (adj > clientAdj) {
if (app.hasShownUi && app != mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -13929,11 +13949,9 @@
app.adjSourceOom = clientAdj;
app.adjTarget = cpr.name;
}
- if (procState > client.curProcState) {
- procState = client.curProcState >
- ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
- ? client.curProcState
- : ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ if (procState > clientProcState) {
+ procState = clientProcState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+ ? clientProcState : ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
}
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -14566,10 +14584,10 @@
// application processes based on their current state.
int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextCachedAdj = curCachedAdj+1;
+ int curClientCachedAdj = curCachedAdj+1;
int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextEmptyAdj = curEmptyAdj+2;
- int curClientCachedAdj = curEmptyAdj;
- for (int i=0; i<N; i++) {
+ for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedBackground && app.thread != null) {
app.procStateChanged = false;
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index e6849ed..b858755 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -339,6 +339,10 @@
}
}
+ boolean isNotResolverActivity() {
+ return !ResolverActivity.class.getName().equals(realActivity.getClassName());
+ }
+
ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
@@ -442,21 +446,22 @@
// If we know the system has determined the component, then
// we can consider this to be a home activity...
- // Note the last check is so we don't count the resolver
- // activity as being home... really, we don't care about
- // doing anything special with something that comes from
- // the core framework package.
- if ((!_componentSpecified || _launchedFromUid == Process.myUid()
+ String homePackageName = supervisor.getHomePackageName();
+ if (homePackageName != null && homePackageName.equals(packageName)) {
+ mActivityType = HOME_ACTIVITY_TYPE;
+ } else if ((!_componentSpecified || _launchedFromUid == Process.myUid()
|| _launchedFromUid == 0) &&
Intent.ACTION_MAIN.equals(_intent.getAction()) &&
_intent.hasCategory(Intent.CATEGORY_HOME) &&
_intent.getCategories().size() == 1 &&
_intent.getData() == null &&
_intent.getType() == null &&
- (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- !ResolverActivity.class.getName().equals(realActivity.getClassName())) {
+ (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
// This sure looks like a home activity!
mActivityType = HOME_ACTIVITY_TYPE;
+ if (isNotResolverActivity()) {
+ supervisor.setHomePackageName(userId, packageName);
+ }
} else if (realActivity.getClassName().contains("com.android.systemui.recent")) {
mActivityType = RECENTS_ACTIVITY_TYPE;
} else {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index a154b9c..dd71044 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -465,7 +465,9 @@
* Returns the top activity in any existing task matching the given
* Intent. Returns null if no such task is found.
*/
- ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+ ActivityRecord findTaskLocked(ActivityRecord target) {
+ Intent intent = target.intent;
+ ActivityInfo info = target.info;
ComponentName cls = intent.getComponent();
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
@@ -474,6 +476,10 @@
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
+ if (task.userId != userId) {
+ // Looking for a different task.
+ continue;
+ }
final ActivityRecord r = task.getTopActivity();
if (r == null || r.finishing || r.userId != userId ||
r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
@@ -518,7 +524,11 @@
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ TaskRecord task = mTaskHistory.get(taskNdx);
+ if (task.userId != mCurrentUser) {
+ return null;
+ }
+ final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = activities.get(activityNdx);
if (!r.finishing && r.intent.getComponent().equals(cls) && r.userId == userId) {
@@ -534,8 +544,7 @@
}
/*
- * Move the activities around in the stack to bring a user to the foreground. This only
- * matters on the home stack. All other stacks are single user.
+ * Move the activities around in the stack to bring a user to the foreground.
* @return whether there are any activities for the specified user.
*/
final boolean switchUserLocked(int userId) {
@@ -1089,6 +1098,9 @@
case RESUMED:
case PAUSING:
case PAUSED:
+ // This case created for transitioning activities from
+ // translucent to opaque {@link Activity#convertToOpaque}.
+ mStackSupervisor.mStoppingActivities.remove(r);
stopActivityLocked(r);
break;
@@ -1172,6 +1184,7 @@
// There are no more activities! Let's just start up the
// Launcher...
ActivityOptions.abort(options);
+ if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: No more activities go home");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return mStackSupervisor.resumeHomeActivity(prev);
}
@@ -1186,6 +1199,7 @@
mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
ActivityOptions.abort(options);
+ if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Top activity resumed " + next);
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -1213,6 +1227,7 @@
final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
mTaskHistory.get(taskNdx).mActivities.get(0).mLaunchHomeTaskNext = true;
} else {
+ if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Launching home next");
return mStackSupervisor.resumeHomeActivity(prev);
}
}
@@ -1227,6 +1242,7 @@
mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
ActivityOptions.abort(options);
+ if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Going to sleep and all paused");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -1255,7 +1271,8 @@
// If we are currently pausing an activity, then don't do anything
// until that is done.
if (!mStackSupervisor.allPausedActivitiesComplete()) {
- if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG, "Skip resume: some activity pausing");
+ if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG,
+ "resumeTopActivityLocked: Skip resume: some activity pausing.");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
@@ -1295,9 +1312,11 @@
if (mResumedActivity != null) {
pausing = true;
startPausingLocked(userLeaving, false);
+ if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity);
}
if (pausing) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
+ if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG,
+ "resumeTopActivityLocked: Skip resume: need to start pausing");
// At this point we want to put the upcoming activity's process
// at the top of the LRU list, since we know we will be needing it
// very soon and it would be a waste to let it get killed if it
@@ -1459,7 +1478,7 @@
// is still at the top and schedule another run if something
// weird happened.
ActivityRecord nextNext = topRunningActivityLocked(null);
- if (DEBUG_SWITCH) Slog.i(TAG,
+ if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG,
"Activity config changed during resume: " + next
+ ", new next: " + nextNext);
if (nextNext != next) {
@@ -1505,6 +1524,7 @@
mStackSupervisor.checkReadyForSleepLocked();
+ if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Resumed " + next);
} catch (Exception e) {
// Whoops, need to restart this activity!
if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to "
@@ -1561,6 +1581,7 @@
}
if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
}
+ if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Restarting " + next);
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
@@ -1568,6 +1589,21 @@
return true;
}
+ private void insertTaskAtTop(TaskRecord task) {
+ mTaskHistory.remove(task);
+ // Now put task at top.
+ int stackNdx = mTaskHistory.size();
+ if (task.userId != mCurrentUser) {
+ // Put non-current user tasks below current user tasks.
+ while (--stackNdx >= 0) {
+ if (mTaskHistory.get(stackNdx).userId != mCurrentUser) {
+ break;
+ }
+ }
+ ++stackNdx;
+ }
+ mTaskHistory.add(stackNdx, task);
+ }
final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
@@ -1577,9 +1613,7 @@
// Last activity in task had been removed or ActivityManagerService is reusing task.
// Insert or replace.
// Might not even be in.
- mTaskHistory.remove(rTask);
- // Now put task at top.
- mTaskHistory.add(rTask);
+ insertTaskAtTop(rTask);
mWindowManager.moveTaskToTop(taskId);
}
TaskRecord task = null;
@@ -1599,7 +1633,8 @@
r.putInHistory();
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
+ (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
+ r.userId);
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
@@ -1660,7 +1695,7 @@
r.updateOptionsLocked(options);
mWindowManager.addAppToken(task.mActivities.indexOf(r),
r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
+ (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId);
boolean doShow = true;
if (newTask) {
// Even though this activity is starting fresh, we still need
@@ -1703,7 +1738,7 @@
// because there is nothing for it to animate on top of.
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
+ (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId);
ActivityOptions.abort(options);
}
if (VALIDATE_TOKENS) {
@@ -2897,8 +2932,7 @@
// Shift all activities with this task up to the top
// of the stack, keeping them in the same internal order.
- mTaskHistory.remove(tr);
- mTaskHistory.add(tr);
+ insertTaskAtTop(tr);
if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr);
if (reason != null &&
@@ -3380,7 +3414,7 @@
printed |= ActivityStackSupervisor.dumpHistoryList(fd, pw,
mTaskHistory.get(taskNdx).mActivities, " ", "Hist", true, !dumpAll,
dumpClient, dumpPackage, needSep, header,
- " Task " + taskNdx + ": id #" + task.taskId);
+ " Task id #" + task.taskId);
if (printed) {
header = null;
}
@@ -3448,12 +3482,8 @@
}
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, boolean toTop) {
- TaskRecord task = new TaskRecord(taskId, info, intent, this);
- if (toTop) {
- mTaskHistory.add(task);
- } else {
- mTaskHistory.add(0, task);
- }
+ TaskRecord task = new TaskRecord(taskId, info, intent);
+ addTask(task, toTop);
return task;
}
@@ -3464,7 +3494,7 @@
void addTask(final TaskRecord task, final boolean toTop) {
task.stack = this;
if (toTop) {
- mTaskHistory.add(task);
+ insertTaskAtTop(task);
} else {
mTaskHistory.add(0, task);
}
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 8d1a665..925fb3f 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -185,9 +185,6 @@
* is being brought in front of us. */
boolean mUserLeaving = false;
- /** Stacks belonging to users other than mCurrentUser. Indexed by userId. */
- final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
-
/** Set when we have taken too long waiting to go to sleep. */
boolean mSleepTimeout = false;
@@ -206,6 +203,12 @@
*/
final PowerManager.WakeLock mGoingToSleep;
+ /**
+ * The name of the current home activity for each user.
+ * TODO: Remove entries when user is deleted.
+ */
+ final SparseArray<String> mHomePackageNames = new SparseArray<String>();
+
public ActivityStackSupervisor(ActivityManagerService service, Context context,
Looper looper) {
mService = service;
@@ -263,8 +266,7 @@
}
boolean isFrontStack(ActivityStack stack) {
- return (stack.mCurrentUser == mCurrentUser) &&
- !(stack.isHomeStack() ^ getFocusedStack().isHomeStack());
+ return !(stack.isHomeStack() ^ getFocusedStack().isHomeStack());
}
void moveHomeStack(boolean toFront) {
@@ -354,7 +356,7 @@
final int stackId = stack.mStackId;
final int nextStackId = mWindowManager.removeStack(stackId);
// TODO: Perhaps we need to let the ActivityManager determine the next focus...
- if (getFocusedStack().mStackId == stackId) {
+ if (mFocusedStack == null || mFocusedStack.mStackId == stackId) {
// If this is the last app stack, set mFocusedStack to null.
mFocusedStack = nextStackId == HOME_STACK_ID ? null : getStack(nextStackId);
}
@@ -467,6 +469,8 @@
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
if (!isFrontStack(stack) && stack.mResumedActivity != null) {
+ if (DEBUG_STATES) Slog.d(TAG, "pauseBackStacks: stack=" + stack +
+ " mResumedActivity=" + stack.mResumedActivity);
stack.startPausingLocked(userLeaving, false);
someActivityPaused = true;
}
@@ -475,16 +479,22 @@
}
boolean allPausedActivitiesComplete() {
+ boolean pausing = true;
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
final ActivityRecord r = stack.mPausingActivity;
if (r != null && r.state != ActivityState.PAUSED
&& r.state != ActivityState.STOPPED
&& r.state != ActivityState.STOPPING) {
- return false;
+ if (DEBUG_STATES) {
+ Slog.d(TAG, "allPausedActivitiesComplete: r=" + r + " state=" + r.state);
+ pausing = false;
+ } else {
+ return false;
+ }
}
}
- return true;
+ return pausing;
}
void reportActivityVisibleLocked(ActivityRecord r) {
@@ -524,8 +534,7 @@
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
- if (stack.mCurrentUser == mCurrentUser && stack != focusedStack &&
- isFrontStack(stack)) {
+ if (stack != focusedStack && isFrontStack(stack)) {
r = stack.topRunningActivityLocked(null);
if (r != null) {
return r;
@@ -895,7 +904,7 @@
r.userId, System.identityHashCode(r),
r.task.taskId, r.shortComponentName);
}
- if (r.isHomeActivity()) {
+ if (r.isHomeActivity() && r.isNotResolverActivity()) {
mService.mHomeProcess = app;
}
mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
@@ -1216,28 +1225,43 @@
return err;
}
- ActivityStack getCorrectStack(ActivityRecord r) {
+ ActivityStack adjustStackFocus(ActivityRecord r) {
final TaskRecord task = r.task;
if (r.isApplicationActivity() || (task != null && task.isApplicationTask())) {
- int stackNdx;
- for (stackNdx = mStacks.size() - 1; stackNdx > 0; --stackNdx) {
- if (mStacks.get(stackNdx).mCurrentUser == mCurrentUser) {
- break;
+ if (task != null) {
+ if (mFocusedStack != task.stack) {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+ "adjustStackFocus: Setting focused stack to r=" + r + " task=" + task);
+ mFocusedStack = task.stack;
+ } else {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+ "adjustStackFocus: Focused stack already=" + mFocusedStack);
+ }
+ return mFocusedStack;
+ }
+
+ if (mFocusedStack != null) {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+ "adjustStackFocus: Have a focused stack=" + mFocusedStack);
+ return mFocusedStack;
+ }
+
+ for (int stackNdx = mStacks.size() - 1; stackNdx > 0; --stackNdx) {
+ ActivityStack stack = mStacks.get(stackNdx);
+ if (!stack.isHomeStack()) {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+ "adjustStackFocus: Setting focused stack=" + stack);
+ mFocusedStack = stack;
+ return mFocusedStack;
}
}
- if (stackNdx == 0) {
- // Time to create the first app stack for this user.
- int stackId = mService.createStack(-1, HOME_STACK_ID,
- StackBox.TASK_STACK_GOES_OVER, 1.0f);
- if (DEBUG_FOCUS) Slog.d(TAG, "getCorrectStack: New stack r=" + r + " stackId="
- + stackId);
- mFocusedStack = getStack(stackId);
- }
- if (task != null) {
- if (DEBUG_FOCUS) Slog.d(TAG, "getCorrectStack: Setting focused stack to r=" +
- r + " task=" + task);
- mFocusedStack = task.stack;
- }
+
+ // Time to create the first app stack for this user.
+ int stackId = mService.createStack(-1, HOME_STACK_ID,
+ StackBox.TASK_STACK_GOES_OVER, 1.0f);
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: New stack r=" + r +
+ " stackId=" + stackId);
+ mFocusedStack = getStack(stackId);
return mFocusedStack;
}
return mHomeStack;
@@ -1256,8 +1280,9 @@
mStackState = STACK_STATE_HOME_TO_FRONT;
}
} else {
- if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: Setting focused stack to r=" +
- r + " task=" + r.task + " Callers=" + Debug.getCallers(3));
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+ "setFocusedStack: Setting focused stack to r=" + r + " task=" + r.task +
+ " Callers=" + Debug.getCallers(3));
mFocusedStack = r.task.stack;
if (mStackState != STACK_STATE_HOME_IN_BACK) {
if (DEBUG_STACK) Slog.d(TAG, "setFocusedStack: mStackState old=" +
@@ -1366,7 +1391,7 @@
// instance of it in the history, and it is always in its own
// unique task, so we do a special search.
ActivityRecord intentActivity = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
- ? findTaskLocked(intent, r.info)
+ ? findTaskLocked(r)
: findActivityLocked(intent, r.info);
if (intentActivity != null) {
if (r.task == null) {
@@ -1592,7 +1617,7 @@
// Should this be considered a new task?
if (r.resultTo == null && !addingToTask
&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- targetStack = getCorrectStack(r);
+ targetStack = adjustStackFocus(r);
moveHomeStack(targetStack.isHomeStack());
if (reuseTask == null) {
r.setTask(targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
@@ -1671,7 +1696,7 @@
// This not being started from an existing activity, and not part
// of a new task... just put it in the top task, though these days
// this case should never happen.
- targetStack = getCorrectStack(r);
+ targetStack = adjustStackFocus(r);
moveHomeStack(targetStack.isHomeStack());
ActivityRecord prev = targetStack.topActivity();
r.setTask(prev != null ? prev.task
@@ -2009,9 +2034,13 @@
resumeTopActivitiesLocked();
}
- ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+ ActivityRecord findTaskLocked(ActivityRecord r) {
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityRecord ar = mStacks.get(stackNdx).findTaskLocked(intent, info);
+ final ActivityStack stack = mStacks.get(stackNdx);
+ if (!r.isApplicationActivity() && !stack.isHomeStack()) {
+ continue;
+ }
+ final ActivityRecord ar = stack.findTaskLocked(r);
if (ar != null) {
return ar;
}
@@ -2193,21 +2222,18 @@
}
boolean switchUserLocked(int userId, UserStartedState uss) {
- mUserStates.put(mCurrentUser, new UserState());
mCurrentUser = userId;
- UserState userState = mUserStates.get(userId);
- if (userState != null) {
- userState.restore();
- mUserStates.delete(userId);
- } else {
- mFocusedStack = null;
- if (DEBUG_STACK) Slog.d(TAG, "switchUserLocked: mStackState=" +
- stackStateToString(STACK_STATE_HOME_IN_FRONT));
- mStackState = STACK_STATE_HOME_IN_FRONT;
+
+ final String homePackageName = mService.getHomePackageName();
+ if (homePackageName != null) {
+ setHomePackageName(mCurrentUser, homePackageName);
}
mStartingUsers.add(uss);
- boolean haveActivities = mHomeStack.switchUserLocked(userId);
+ boolean haveActivities = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ haveActivities |= mStacks.get(stackNdx).switchUserLocked(userId);
+ }
resumeTopActivitiesLocked();
@@ -2304,6 +2330,12 @@
pw.print(prefix); pw.print("mStackState="); pw.println(stackStateToString(mStackState));
pw.print(prefix); pw.println("mSleepTimeout: " + mSleepTimeout);
pw.print(prefix); pw.println("mCurTaskId: " + mCurTaskId);
+ pw.print(prefix); pw.print("mHomePackageNames:");
+ for (int i = 0; i < mHomePackageNames.size(); ++i) {
+ pw.print(" ("); pw.print(mHomePackageNames.keyAt(i)); pw.print(",");
+ pw.print(mHomePackageNames.valueAt(i)); pw.print(")");
+ }
+ pw.println();
}
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -2558,22 +2590,13 @@
}
}
- private final class UserState {
- final ActivityStack mSavedFocusedStack;
- final int mSavedStackState;
+ String getHomePackageName() {
+ return mHomePackageNames.get(mCurrentUser);
+ }
- public UserState() {
- ActivityStackSupervisor supervisor = ActivityStackSupervisor.this;
- mSavedFocusedStack = supervisor.mFocusedStack;
- mSavedStackState = supervisor.mStackState;
- }
-
- void restore() {
- ActivityStackSupervisor supervisor = ActivityStackSupervisor.this;
- supervisor.mFocusedStack = mSavedFocusedStack;
- if (DEBUG_STACK) Slog.d(TAG, "UserState.restore: mStackState old=" +
- stackStateToString(mSavedStackState));
- supervisor.mStackState = mSavedStackState;
- }
+ void setHomePackageName(int userId, String homePackageName) {
+ if (DEBUG_SWITCH) Slog.d(TAG, "setHomePackageName: user=" + userId + " package="
+ + homePackageName);
+ mHomePackageNames.put(userId, homePackageName);
}
}
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index dd81493..576adc2 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -89,12 +89,15 @@
if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
sb.append("ACT ");
}
- if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
- sb.append("!VIS ");
- }
if ((flags&Context.BIND_VISIBLE) != 0) {
sb.append("VIS ");
}
+ if ((flags&Context.BIND_SHOWING_UI) != 0) {
+ sb.append("UI ");
+ }
+ if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
+ sb.append("!VIS ");
+ }
if (serviceDead) {
sb.append("DEAD ");
}
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index 8b0466f..106ba22 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -178,7 +178,7 @@
void applyDisplaySize(WindowManagerService wm) {
if (!mHaveDisplaySize) {
Point p = new Point();
- wm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, p);
+ wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);
if (p.x != 0 && p.y != 0) {
updateOomLevels(p.x, p.y, true);
mHaveDisplaySize = true;
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 5c1b7f24..1b45d30 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -207,8 +207,8 @@
pw.println(starting);
pw.print(prefix); pw.print("lastActivityTime=");
TimeUtils.formatDuration(lastActivityTime, now, pw);
- pw.print(" lruWeight="); pw.print(lruWeight);
- pw.print(" serviceb="); pw.print(serviceb);
+ pw.print(" lruWeight="); pw.println(lruWeight);
+ pw.print(prefix); pw.print("serviceb="); pw.print(serviceb);
pw.print(" keeping="); pw.print(keeping);
pw.print(" cached="); pw.print(cached);
pw.print(" empty="); pw.println(empty);
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index d79211c..ebcf22a 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -59,11 +59,10 @@
private boolean mApplicationTask = true;
- TaskRecord(int _taskId, ActivityInfo info, Intent _intent, ActivityStack _stack) {
+ TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
taskId = _taskId;
affinity = info.taskAffinity;
setIntent(_intent, info);
- stack = _stack;
}
void touchActiveTime() {
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 87263b30..22a7841 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -152,6 +152,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
mContext.registerReceiver(mStateReceiver, filter);
filter = new IntentFilter();
@@ -511,6 +512,8 @@
if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
}
+ } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+ updateConfiguration();
}
}
}
@@ -613,7 +616,7 @@
public int[] getUpstreamIfaceTypes() {
int values[];
synchronized (mPublicSync) {
- updateConfiguration();
+ updateConfiguration(); // TODO - remove?
values = new int[mUpstreamIfaceTypes.size()];
Iterator<Integer> iterator = mUpstreamIfaceTypes.iterator();
for (int i=0; i < mUpstreamIfaceTypes.size(); i++) {
@@ -1284,7 +1287,7 @@
int upType = ConnectivityManager.TYPE_NONE;
String iface = null;
- updateConfiguration();
+ updateConfiguration(); // TODO - remove?
synchronized (mPublicSync) {
if (VDBG) {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 4791ec0..38453c8 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -892,7 +892,8 @@
for (int i=0; i<newWork.size(); i++) {
try {
int uid = newWork.get(i);
- mAppOpsService.startOperation(AppOpsManager.OP_GPS, uid, newWork.getName(i));
+ mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
+ AppOpsManager.OP_GPS, uid, newWork.getName(i));
if (uid != lastuid) {
lastuid = uid;
mBatteryStats.noteStartGps(uid);
@@ -909,7 +910,8 @@
for (int i=0; i<goneWork.size(); i++) {
try {
int uid = goneWork.get(i);
- mAppOpsService.finishOperation(AppOpsManager.OP_GPS, uid, goneWork.getName(i));
+ mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
+ AppOpsManager.OP_GPS, uid, goneWork.getName(i));
if (uid != lastuid) {
lastuid = uid;
mBatteryStats.noteStopGps(uid);
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 5074409..05eeb36 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -154,7 +154,7 @@
private final Context mContext;
private final INetworkManagementService mNetworkManager;
- private final IAlarmManager mAlarmManager;
+ private final AlarmManager mAlarmManager;
private final TrustedTime mTime;
private final TelephonyManager mTeleManager;
private final NetworkStatsSettings mSettings;
@@ -261,10 +261,10 @@
NetworkStatsSettings settings) {
mContext = checkNotNull(context, "missing Context");
mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
- mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager");
mTime = checkNotNull(time, "missing TrustedTime");
mTeleManager = checkNotNull(TelephonyManager.getDefault(), "missing TelephonyManager");
mSettings = checkNotNull(settings, "missing NetworkStatsSettings");
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
final PowerManager powerManager = (PowerManager) context.getSystemService(
Context.POWER_SERVICE);
@@ -420,20 +420,16 @@
* reschedule based on current {@link NetworkStatsSettings#getPollInterval()}.
*/
private void registerPollAlarmLocked() {
- try {
- if (mPollIntent != null) {
- mAlarmManager.remove(mPollIntent);
- }
-
- mPollIntent = PendingIntent.getBroadcast(
- mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
-
- final long currentRealtime = SystemClock.elapsedRealtime();
- mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
- mSettings.getPollInterval(), mPollIntent);
- } catch (RemoteException e) {
- // ignored; service lives in system_server
+ if (mPollIntent != null) {
+ mAlarmManager.cancel(mPollIntent);
}
+
+ mPollIntent = PendingIntent.getBroadcast(
+ mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
+
+ final long currentRealtime = SystemClock.elapsedRealtime();
+ mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
+ mSettings.getPollInterval(), mPollIntent);
}
/**
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 6f57261..7a01219 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -6189,9 +6189,9 @@
PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
if (UserHandle.getUserId(uid) != userId) {
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "setApplicationBlocked for user " + userId);
+ "setApplicationBlockedSetting for user " + userId);
}
if (blocked && isPackageDeviceAdmin(packageName, userId)) {
@@ -6224,6 +6224,8 @@
return true;
}
if (sendRemoved) {
+ killApplication(packageName, UserHandle.getUid(userId, pkgSetting.appId),
+ "blocking pkg");
sendPackageBlockedForUser(packageName, pkgSetting, userId);
}
} finally {
@@ -10016,6 +10018,7 @@
}
}
}
+ sUserManager.systemReady();
}
public boolean isSafeMode() {
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 2901212..16c2fe7 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -21,11 +21,13 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.ActivityThread;
import android.app.IStopUserCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.RestrictionEntry;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
@@ -51,6 +53,7 @@
import android.util.TimeUtils;
import android.util.Xml;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
@@ -229,6 +232,13 @@
sInstance = this;
}
}
+
+ }
+
+ void systemReady() {
+ mUserPackageMonitor.register(ActivityThread.systemMain().getSystemContext(),
+ null, UserHandle.ALL, false);
+ userForeground(UserHandle.USER_OWNER);
}
@Override
@@ -822,11 +832,6 @@
pinState.failedAttempts = failedAttempts;
pinState.lastAttemptTime = lastAttemptTime;
}
- // If this is not a restricted profile and there is no restrictions pin, clean up
- // any restrictions files that might have been left behind.
- if (!userInfo.isRestricted() && salt == 0) {
- cleanAppRestrictions(id);
- }
return userInfo;
} catch (IOException ioe) {
@@ -878,11 +883,22 @@
}
}
+ private boolean isPackageInstalled(String pkg, int userId) {
+ final ApplicationInfo info = mPm.getApplicationInfo(pkg,
+ PackageManager.GET_UNINSTALLED_PACKAGES,
+ userId);
+ if (info == null || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+ return false;
+ }
+ return true;
+ }
+
/**
- * Removes all the restrictions files (res_<packagename>) for a given user.
+ * Removes all the restrictions files (res_<packagename>) for a given user, if all is true,
+ * else removes only those packages that have been uninstalled.
* Does not do any permissions checking.
*/
- private void cleanAppRestrictions(int userId) {
+ private void cleanAppRestrictions(int userId, boolean all) {
synchronized (mPackagesLock) {
File dir = Environment.getUserSystemDirectory(userId);
String[] files = dir.list();
@@ -891,13 +907,33 @@
if (fileName.startsWith(RESTRICTIONS_FILE_PREFIX)) {
File resFile = new File(dir, fileName);
if (resFile.exists()) {
- resFile.delete();
+ if (all) {
+ resFile.delete();
+ } else {
+ String pkg = fileName.substring(RESTRICTIONS_FILE_PREFIX.length());
+ if (!isPackageInstalled(pkg, userId)) {
+ resFile.delete();
+ }
+ }
}
}
}
}
}
+ /**
+ * Removes the app restrictions file for a specific package and user id, if it exists.
+ */
+ private void cleanAppRestrictionsForPackage(String pkg, int userId) {
+ synchronized (mPackagesLock) {
+ File dir = Environment.getUserSystemDirectory(userId);
+ File resFile = new File(dir, RESTRICTIONS_FILE_PREFIX + pkg);
+ if (resFile.exists()) {
+ resFile.delete();
+ }
+ }
+ }
+
@Override
public UserInfo createUser(String name, int flags) {
checkManageUsersPermission("Only the system can create users");
@@ -1168,6 +1204,40 @@
return true;
}
+ @Override
+ public void removeRestrictions() {
+ checkManageUsersPermission("Only system can remove restrictions");
+ final int userHandle = UserHandle.getCallingUserId();
+ synchronized (mPackagesLock) {
+ // Remove all user restrictions
+ setUserRestrictions(new Bundle(), userHandle);
+ // Remove restrictions pin
+ changeRestrictionsPin(null);
+ // Remove any app restrictions
+ cleanAppRestrictions(userHandle, true);
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ List<ApplicationInfo> apps =
+ mPm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES,
+ userHandle).getList();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ for (ApplicationInfo appInfo : apps) {
+ if ((appInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0
+ && (appInfo.flags & ApplicationInfo.FLAG_BLOCKED) != 0) {
+ mPm.setApplicationBlockedSettingAsUser(appInfo.packageName, false,
+ userHandle);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ });
+ }
+
/*
* Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
* Not the most secure, but it is at least a second level of protection. First level is that
@@ -1372,7 +1442,7 @@
}
/**
- * Make a note of the last started time of a user.
+ * Make a note of the last started time of a user and do some cleanup.
* @param userId the user that was just foregrounded
*/
public void userForeground(int userId) {
@@ -1387,6 +1457,12 @@
user.lastLoggedInTime = now;
writeUserLocked(user);
}
+ // If this is not a restricted profile and there is no restrictions pin, clean up
+ // all restrictions files that might have been left behind, else clean up just the
+ // ones with uninstalled packages
+ RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
+ final long salt = pinState == null ? 0 : pinState.salt;
+ cleanAppRestrictions(userId, (!user.isRestricted() && salt == 0));
}
}
@@ -1453,4 +1529,17 @@
}
}
}
+
+ private PackageMonitor mUserPackageMonitor = new PackageMonitor() {
+ @Override
+ public void onPackageRemoved(String pkg, int uid) {
+ final int userId = this.getChangingUserId();
+ // Package could be disappearing because it is being blocked, so also check if
+ // it has been uninstalled.
+ final boolean uninstalled = isPackageDisappearing(pkg) == PACKAGE_PERMANENT_CHANGE;
+ if (uninstalled && userId >= 0 && !isPackageInstalled(pkg, userId)) {
+ cleanAppRestrictionsForPackage(pkg, userId);
+ }
+ }
+ };
}
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
index e44cfe5..264e2e9 100644
--- a/services/java/com/android/server/power/Notifier.java
+++ b/services/java/com/android/server/power/Notifier.java
@@ -144,7 +144,8 @@
} else {
mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, monitorType);
// XXX need to deal with disabled operations.
- mAppOps.startOperation(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+ mAppOps.startOperation(AppOpsManager.getToken(mAppOps),
+ AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
}
} catch (RemoteException ex) {
// Ignore
@@ -169,7 +170,8 @@
mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, monitorType);
} else {
mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, monitorType);
- mAppOps.finishOperation(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+ mAppOps.finishOperation(AppOpsManager.getToken(mAppOps),
+ AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
}
} catch (RemoteException ex) {
// Ignore
diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java
index b9e0280..203bc86 100644
--- a/services/java/com/android/server/print/RemotePrintService.java
+++ b/services/java/com/android/server/print/RemotePrintService.java
@@ -105,7 +105,7 @@
throwIfDestroyed();
if (isBound()) {
if (DEBUG) {
- Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnAllPrintJobsHandled");
+ Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnAllPrintJobsHandled()");
}
// If bound and all the work is completed, then unbind.
ensureUnbound();
@@ -342,7 +342,7 @@
final long identity = Binder.clearCallingIdentity();
try {
return service.mSpooler.getPrintJobInfos(service.mComponentName,
- PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY);
+ PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS, PrintManager.APP_ID_ANY);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java
index bf2c8e7..6445c2e 100644
--- a/services/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/java/com/android/server/print/RemotePrintSpooler.java
@@ -296,8 +296,7 @@
}
mContext.bindServiceAsUser(mIntent, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT,
- mUserHandle);
+ Context.BIND_AUTO_CREATE, mUserHandle);
final long startMillis = SystemClock.uptimeMillis();
while (true) {
diff --git a/services/java/com/android/server/updates/CarrierProvisioningUrlsInstallReceiver.java b/services/java/com/android/server/updates/CarrierProvisioningUrlsInstallReceiver.java
new file mode 100644
index 0000000..b53fb65
--- /dev/null
+++ b/services/java/com/android/server/updates/CarrierProvisioningUrlsInstallReceiver.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.updates;
+
+public class CarrierProvisioningUrlsInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ public CarrierProvisioningUrlsInstallReceiver() {
+ super("/data/misc/radio/", "provisioning_urls.xml", "metadata/", "version");
+ }
+}
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 088061c..4f699ae 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -25,7 +25,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.util.Slog;
-import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
@@ -106,8 +105,6 @@
/** Detect user tapping outside of current focused stack bounds .*/
Region mTouchExcludeRegion = new Region();
- SparseArray<UserStacks> mUserStacks = new SparseArray<UserStacks>();
-
/** Save allocating when retrieving tasks */
ArrayList<Task> mTmpTasks = new ArrayList<Task>();
@@ -166,22 +163,6 @@
*/
ArrayList<Task> getTasks() {
mTmpTasks.clear();
- // First do the tasks belonging to other users.
- final int numUserStacks = mUserStacks.size();
- for (int i = 0; i < numUserStacks; ++i) {
- UserStacks userStacks = mUserStacks.valueAt(i);
- ArrayList<TaskStack> stacks = userStacks.mSavedStackHistory;
- final int numStacks = stacks.size();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- TaskStack stack = stacks.get(stackNdx);
- if (stack != mHomeStack) {
- if (WindowManagerService.DEBUG_LAYERS) Slog.i(TAG, "getTasks: mStackHistory=" +
- mStackHistory);
- mTmpTasks.addAll(stack.getTasks());
- }
- }
- }
- // Now do the current user's tasks.
final int numStacks = mStackHistory.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
mTmpTasks.addAll(mStackHistory.get(stackNdx).getTasks());
@@ -359,14 +340,6 @@
return bounds;
}
}
- // Not in the visible stacks, try the saved ones.
- for (int userNdx = mUserStacks.size() - 1; userNdx >= 0; --userNdx) {
- UserStacks userStacks = mUserStacks.valueAt(userNdx);
- Rect bounds = userStacks.mSavedStackBox.getStackBounds(stackId);
- if (bounds != null) {
- return bounds;
- }
- }
return null;
}
@@ -400,12 +373,9 @@
win.hideLw(false);
}
}
- // Clear the old user's non-home StackBox
- mUserStacks.put(oldUserId, new UserStacks());
- UserStacks userStacks = mUserStacks.get(newUserId);
- if (userStacks != null) {
- userStacks.restore();
- mUserStacks.delete(newUserId);
+
+ for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+ mStackBoxes.get(stackBoxNdx).switchUserStacks(newUserId);
}
}
@@ -508,49 +478,6 @@
token.dump(pw, " ");
}
}
- if (mUserStacks.size() > 0) {
- pw.println();
- pw.println(" Saved user stacks:");
- for (int i = 0; i < mUserStacks.size(); ++i) {
- UserStacks userStacks = mUserStacks.valueAt(i);
- pw.print(" UserId="); pw.println(Integer.toHexString(mUserStacks.keyAt(i)));
- pw.print(" StackHistory="); pw.println(userStacks.mSavedStackHistory);
- pw.print(" StackBox="); userStacks.mSavedStackBox.dump(" ", pw);
- }
- }
pw.println();
}
-
- private final class UserStacks {
- final ArrayList<TaskStack> mSavedStackHistory;
- StackBox mSavedStackBox;
- int mBoxNdx;
-
- public UserStacks() {
- mSavedStackHistory = new ArrayList<TaskStack>(mStackHistory);
- for (int stackNdx = mStackHistory.size() - 1; stackNdx >=0; --stackNdx) {
- if (mStackHistory.get(stackNdx) != mHomeStack) {
- mStackHistory.remove(stackNdx);
- }
- }
- mSavedStackBox = null;
- mBoxNdx = -1;
- for (int boxNdx = mStackBoxes.size() - 1; boxNdx >= 0; --boxNdx) {
- StackBox box = mStackBoxes.get(boxNdx);
- if (box.mStack != mHomeStack) {
- mSavedStackBox = box;
- mBoxNdx = boxNdx;
- mStackBoxes.remove(boxNdx);
- break;
- }
- }
- }
-
- void restore() {
- mStackHistory = mSavedStackHistory;
- if (mBoxNdx >= 0) {
- mStackBoxes.add(mBoxNdx, mSavedStackBox);
- }
- }
- }
}
diff --git a/services/java/com/android/server/wm/StackBox.java b/services/java/com/android/server/wm/StackBox.java
index d352464..d4cf4e2 100644
--- a/services/java/com/android/server/wm/StackBox.java
+++ b/services/java/com/android/server/wm/StackBox.java
@@ -109,8 +109,8 @@
* @return true if the specified StackBox matches this or one of its descendants.
*/
boolean contains(int stackBoxId) {
- return mStackBoxId == stackBoxId || mFirst.contains(stackBoxId)
- || mSecond.contains(stackBoxId);
+ return mStackBoxId == stackBoxId ||
+ (mStack == null && (mFirst.contains(stackBoxId) || mSecond.contains(stackBoxId)));
}
/**
@@ -371,6 +371,15 @@
mSecond.stopDimmingIfNeeded();
}
+ void switchUserStacks(int userId) {
+ if (mStack != null) {
+ mStack.switchUser(userId);
+ return;
+ }
+ mFirst.switchUserStacks(userId);
+ mSecond.switchUserStacks(userId);
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mParent="); pw.println(mParent);
pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
diff --git a/services/java/com/android/server/wm/Task.java b/services/java/com/android/server/wm/Task.java
index 88eb96e..d9acbb9 100644
--- a/services/java/com/android/server/wm/Task.java
+++ b/services/java/com/android/server/wm/Task.java
@@ -21,11 +21,13 @@
TaskStack mStack;
final AppTokenList mAppTokens = new AppTokenList();
final int taskId;
+ final int mUserId;
- Task(AppWindowToken wtoken, TaskStack stack) {
+ Task(AppWindowToken wtoken, TaskStack stack, int userId) {
taskId = wtoken.groupId;
mAppTokens.add(wtoken);
mStack = stack;
+ mUserId = userId;
}
DisplayContent getDisplayContent() {
diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java
index 18019e6..29156be 100644
--- a/services/java/com/android/server/wm/TaskStack.java
+++ b/services/java/com/android/server/wm/TaskStack.java
@@ -97,9 +97,28 @@
* @param toTop Whether to add it to the top or bottom.
*/
boolean addTask(Task task, boolean toTop) {
- if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop);
mStackBox.makeDirty();
- mTasks.add(toTop ? mTasks.size() : 0, task);
+
+ int stackNdx;
+ if (!toTop) {
+ stackNdx = 0;
+ } else {
+ stackNdx = mTasks.size();
+ final int currentUserId = mService.mCurrentUserId;
+ if (task.mUserId != currentUserId) {
+ // Place the task below all current user tasks.
+ while (--stackNdx >= 0) {
+ if (currentUserId != mTasks.get(stackNdx).mUserId) {
+ break;
+ }
+ }
+ ++stackNdx;
+ }
+ }
+ if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
+ + " pos=" + stackNdx);
+ mTasks.add(stackNdx, task);
+
task.mStack = this;
return mDisplayContent.moveHomeStackBox(mStackId == HOME_STACK_ID);
}
@@ -256,6 +275,18 @@
}
}
+ void switchUser(int userId) {
+ int top = mTasks.size();
+ for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
+ Task task = mTasks.get(taskNdx);
+ if (task.mUserId == userId) {
+ mTasks.remove(taskNdx);
+ mTasks.add(task);
+ --top;
+ }
+ }
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index afdbc87..2688cff 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -542,7 +542,7 @@
boolean hasPendingLayoutChanges = false;
final int numDisplays = mService.mDisplayContents.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent displayContent = mService.mDisplayContents.get(displayNdx);
+ final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
final int pendingChanges = getPendingLayoutChanges(displayContent.getDisplayId());
if ((pendingChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 3e7509a..f763068 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -283,6 +283,9 @@
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ private static final String DENSITY_OVERRIDE = "ro.config.density_override";
+ private static final String SIZE_OVERRIDE = "ro.config.size_override";
+
private static final int MAX_SCREENSHOT_RETRIES = 3;
final private KeyguardDisableHandler mKeyguardDisableHandler;
@@ -3371,7 +3374,7 @@
@Override
public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
- int requestedOrientation, boolean fullscreen, boolean showWhenLocked) {
+ int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3404,7 +3407,7 @@
atoken.showWhenLocked = showWhenLocked;
atoken.requestedOrientation = requestedOrientation;
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
- + " at " + addPos);
+ + " to stack=" + stackId + " task=" + taskId + " at " + addPos);
Task task = mTaskIdToTask.get(taskId);
if (task == null) {
@@ -3412,7 +3415,7 @@
if (stack == null) {
throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);
}
- task = new Task(atoken, stack);
+ task = new Task(atoken, stack, userId);
stack.addTask(task, true);
stack.getDisplayContent().moveStack(stack, true);
mTaskIdToTask.put(taskId, task);
@@ -5069,13 +5072,11 @@
throw new SecurityException("Requires SET_ANIMATION_SCALE permission");
}
- if (scale < 0) scale = 0;
- else if (scale > 20) scale = 20;
- scale = Math.abs(scale);
+ scale = fixScale(scale);
switch (which) {
- case 0: mWindowAnimationScale = fixScale(scale); break;
- case 1: mTransitionAnimationScale = fixScale(scale); break;
- case 2: mAnimatorDurationScale = fixScale(scale); break;
+ case 0: mWindowAnimationScale = scale; break;
+ case 1: mTransitionAnimationScale = scale; break;
+ case 2: mAnimatorDurationScale = scale; break;
}
// Persist setting
@@ -7626,8 +7627,11 @@
}
private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) {
- final String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
+ String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.DISPLAY_SIZE_FORCED);
+ if (sizeStr == null) {
+ sizeStr = SystemProperties.get(SIZE_OVERRIDE, null);
+ }
if (sizeStr != null && sizeStr.length() > 0) {
final int pos = sizeStr.indexOf(',');
if (pos > 0 && sizeStr.lastIndexOf(',') == pos) {
@@ -7647,8 +7651,11 @@
}
}
}
- final String densityStr = Settings.Global.getString(mContext.getContentResolver(),
+ String densityStr = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.DISPLAY_DENSITY_FORCED);
+ if (densityStr == null) {
+ densityStr = SystemProperties.get(DENSITY_OVERRIDE, null);
+ }
if (densityStr != null && densityStr.length() > 0) {
int density;
try {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index cdc4d78..aca77b8 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -878,8 +878,7 @@
mAlarmManager.remove(isA(PendingIntent.class));
expectLastCall().anyTimes();
- mAlarmManager.setInexactRepeating(
- eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class));
+ mAlarmManager.set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(), isA(PendingIntent.class));
expectLastCall().atLeastOnce();
mNetManager.setGlobalAlert(anyLong());
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 53318a4..e4c4214 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
}
try {
- mWm.addAppToken(0, null, 0, 0, 0, false, false);
+ mWm.addAppToken(0, null, 0, 0, 0, false, false, 0);
fail("IWindowManager.addAppToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index b27ce0e..4e73568 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -25,8 +25,8 @@
# We need to process the framework classes.jar file, but we can't
# depend directly on it (private vars won't be inherited correctly).
# So, we depend on framework's BUILT file.
-built_framework_dep := $(call java-lib-deps,framework)
-built_framework_classes := $(call java-lib-files,framework)
+built_framework_dep := $(call java-lib-deps,framework-base)
+built_framework_classes := $(call java-lib-files,framework-base)
built_core_dep := $(call java-lib-deps,core)
built_core_classes := $(call java-lib-files,core)
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 6343049..f0c3a75 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -81,7 +81,7 @@
@Override
public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
- boolean arg5, boolean arg6)
+ boolean arg5, boolean arg6, int arg7)
throws RemoteException {
// TODO Auto-generated method stub
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index cbefd3d..b909bec 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -30,6 +30,7 @@
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.Density;
import com.android.resources.ResourceType;
+import com.android.resources.ScreenOrientation;
import com.android.resources.ScreenSize;
import android.content.res.Configuration;
@@ -347,6 +348,23 @@
config.compatScreenWidthDp = config.screenWidthDp;
config.compatScreenHeightDp = config.screenHeightDp;
+ ScreenOrientation orientation = hardwareConfig.getOrientation();
+ if (orientation != null) {
+ switch (orientation) {
+ case PORTRAIT:
+ config.orientation = Configuration.ORIENTATION_PORTRAIT;
+ break;
+ case LANDSCAPE:
+ config.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ break;
+ case SQUARE:
+ config.orientation = Configuration.ORIENTATION_SQUARE;
+ break;
+ }
+ } else {
+ config.orientation = Configuration.ORIENTATION_UNDEFINED;
+ }
+
// TODO: fill in more config info.
return config;