Merge "Fix the display name of InputMethodSubtype"
diff --git a/Android.mk b/Android.mk
index e987f91..90ffcd3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -127,11 +127,13 @@
core/java/android/os/INetworkManagementService.aidl \
core/java/android/os/IPermissionController.aidl \
core/java/android/os/IPowerManager.aidl \
- core/java/android/os/IRemoteCallback.aidl \
+ core/java/android/os/IRemoteCallback.aidl \
core/java/android/os/IVibratorService.aidl \
- core/java/android/service/wallpaper/IWallpaperConnection.aidl \
- core/java/android/service/wallpaper/IWallpaperEngine.aidl \
- core/java/android/service/wallpaper/IWallpaperService.aidl \
+ core/java/android/service/wallpaper/IWallpaperConnection.aidl \
+ core/java/android/service/wallpaper/IWallpaperEngine.aidl \
+ core/java/android/service/wallpaper/IWallpaperService.aidl \
+ core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
+ core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
core/java/android/view/accessibility/IAccessibilityManager.aidl \
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
diff --git a/api/14.txt b/api/14.txt
index 58d49d9..ac93afb 100644
--- a/api/14.txt
+++ b/api/14.txt
@@ -22250,10 +22250,8 @@
public class AccessibilityRecord {
ctor protected AccessibilityRecord();
- method protected void clear();
method public int getAddedCount();
method public java.lang.CharSequence getBeforeText();
- method public boolean getBooleanProperty(int);
method public java.lang.CharSequence getClassName();
method public java.lang.CharSequence getContentDescription();
method public int getCurrentItemIndex();
diff --git a/api/current.txt b/api/current.txt
index afaf385..80d0687 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -90,6 +90,7 @@
field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+ field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -485,7 +486,6 @@
field public static final int hint = 16843088; // 0x1010150
field public static final int homeAsUpIndicator = 16843531; // 0x101030b
field public static final int homeLayout = 16843549; // 0x101031d
- field public static final int horizontalDirection = 16843631; // 0x101036f
field public static final int horizontalDivider = 16843053; // 0x101012d
field public static final int horizontalGap = 16843327; // 0x101023f
field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
@@ -570,6 +570,7 @@
field public static final int layerType = 16843604; // 0x1010354
field public static final int layout = 16842994; // 0x10100f2
field public static final int layoutAnimation = 16842988; // 0x10100ec
+ field public static final int layoutDirection = 16843631; // 0x101036f
field public static final int layout_above = 16843140; // 0x1010184
field public static final int layout_alignBaseline = 16843142; // 0x1010186
field public static final int layout_alignBottom = 16843146; // 0x101018a
@@ -1515,12 +1516,13 @@
field public static final int Theme_Holo_Light_Dialog_NoActionBar = 16973941; // 0x1030075
field public static final int Theme_Holo_Light_Dialog_NoActionBar_MinWidth = 16973942; // 0x1030076
field public static final int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0
+ field public static final int Theme_Holo_Light_NoActionBar_Fullscreen = 16974065; // 0x10300f1
field public static final int Theme_Holo_Light_Panel = 16973948; // 0x103007c
- field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974067; // 0x10300f3
+ field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974068; // 0x10300f4
field public static final int Theme_Holo_NoActionBar = 16973932; // 0x103006c
field public static final int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d
field public static final int Theme_Holo_Panel = 16973947; // 0x103007b
- field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974066; // 0x10300f2
+ field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974067; // 0x10300f3
field public static final int Theme_Holo_Wallpaper = 16973949; // 0x103007d
field public static final int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e
field public static final int Theme_InputMethod = 16973908; // 0x1030054
@@ -4966,6 +4968,7 @@
field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED";
field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN";
+ field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
field public static final java.lang.String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
@@ -5014,8 +5017,8 @@
field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET";
field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
- field public static final java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
- field public static final java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
+ field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
+ field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
field public static final java.lang.String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT";
field public static final java.lang.String ACTION_VIEW = "android.intent.action.VIEW";
field public static final java.lang.String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
@@ -16438,6 +16441,7 @@
method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
method public void syncAll(int);
field public static final int USAGE_GRAPHICS_CONSTANTS = 8; // 0x8
+ field public static final int USAGE_GRAPHICS_RENDER_TARGET = 16; // 0x10
field public static final int USAGE_GRAPHICS_TEXTURE = 2; // 0x2
field public static final int USAGE_GRAPHICS_VERTEX = 4; // 0x4
field public static final int USAGE_SCRIPT = 1; // 0x1
@@ -20435,14 +20439,12 @@
method public static int getAbsoluteGravity(int, boolean);
method public static boolean isHorizontal(int);
method public static boolean isVertical(int);
- field public static final int AFTER = 8388613; // 0x800005
field public static final int AXIS_CLIP = 8; // 0x8
field public static final int AXIS_PULL_AFTER = 4; // 0x4
field public static final int AXIS_PULL_BEFORE = 2; // 0x2
field public static final int AXIS_SPECIFIED = 1; // 0x1
field public static final int AXIS_X_SHIFT = 0; // 0x0
field public static final int AXIS_Y_SHIFT = 4; // 0x4
- field public static final int BEFORE = 8388611; // 0x800003
field public static final int BOTTOM = 80; // 0x50
field public static final int CENTER = 17; // 0x11
field public static final int CENTER_HORIZONTAL = 1; // 0x1
@@ -20451,15 +20453,17 @@
field public static final int CLIP_VERTICAL = 128; // 0x80
field public static final int DISPLAY_CLIP_HORIZONTAL = 16777216; // 0x1000000
field public static final int DISPLAY_CLIP_VERTICAL = 268435456; // 0x10000000
+ field public static final int END = 8388613; // 0x800005
field public static final int FILL = 119; // 0x77
field public static final int FILL_HORIZONTAL = 7; // 0x7
field public static final int FILL_VERTICAL = 112; // 0x70
field public static final int HORIZONTAL_GRAVITY_MASK = 7; // 0x7
field public static final int LEFT = 3; // 0x3
field public static final int NO_GRAVITY = 0; // 0x0
- field public static final int RELATIVE_HORIZONTAL_DIRECTION = 8388608; // 0x800000
field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+ field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
field public static final int RIGHT = 5; // 0x5
+ field public static final int START = 8388611; // 0x800003
field public static final int TOP = 48; // 0x30
field public static final int VERTICAL_GRAVITY_MASK = 112; // 0x70
}
@@ -21477,6 +21481,7 @@
method protected int computeVerticalScrollExtent();
method protected int computeVerticalScrollOffset();
method protected int computeVerticalScrollRange();
+ method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
method public void destroyDrawingCache();
method public void dispatchConfigurationChanged(android.content.res.Configuration);
@@ -21505,6 +21510,7 @@
method public android.view.View findFocus();
method public final android.view.View findViewById(int);
method public final android.view.View findViewWithTag(java.lang.Object);
+ method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence);
method protected boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
method public void forceLayout();
@@ -21673,6 +21679,7 @@
method public boolean onGenericMotionEvent(android.view.MotionEvent);
method public boolean onHoverEvent(android.view.MotionEvent);
method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+ method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
@@ -22566,17 +22573,21 @@
method public void appendRecord(android.view.accessibility.AccessibilityRecord);
method public int describeContents();
method public static java.lang.String eventTypeToString(int);
+ method public int getAccessibilityWindowId();
method public long getEventTime();
method public int getEventType();
method public java.lang.CharSequence getPackageName();
method public android.view.accessibility.AccessibilityRecord getRecord(int);
method public int getRecordCount();
+ method public android.view.accessibility.AccessibilityNodeInfo getSource();
method public void initFromParcel(android.os.Parcel);
method public static android.view.accessibility.AccessibilityEvent obtain(int);
+ method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
method public static android.view.accessibility.AccessibilityEvent obtain();
method public void setEventTime(long);
method public void setEventType(int);
method public void setPackageName(java.lang.CharSequence);
+ method public void setSource(android.view.View);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int INVALID_POSITION = -1; // 0xffffffff
@@ -22601,20 +22612,75 @@
}
public final class AccessibilityManager {
+ method public boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int);
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
method public void interrupt();
method public boolean isEnabled();
+ method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
}
+ public static abstract interface AccessibilityManager.AccessibilityStateChangeListener {
+ method public abstract void onAccessibilityStateChanged(boolean);
+ }
+
+ public class AccessibilityNodeInfo implements android.os.Parcelable {
+ method public void addAction(int);
+ method public void addChild(android.view.View);
+ method public int describeContents();
+ method public int getAccessibilityWindowId();
+ method public int getActions();
+ method public void getBounds(android.graphics.Rect);
+ method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
+ method public int getChildCount();
+ method public java.lang.CharSequence getClassName();
+ method public java.lang.CharSequence getContentDescription();
+ method public java.lang.CharSequence getPackageName();
+ method public android.view.accessibility.AccessibilityNodeInfo getParent();
+ method public java.lang.CharSequence getText();
+ method public boolean isCheckable();
+ method public boolean isChecked();
+ method public boolean isClickable();
+ method public boolean isEnabled();
+ method public boolean isFocusable();
+ method public boolean isFocused();
+ method public boolean isLongClickable();
+ method public boolean isPassword();
+ method public boolean isSelected();
+ method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
+ method public static android.view.accessibility.AccessibilityNodeInfo obtain();
+ method public boolean performAction(int);
+ method public void recycle();
+ method public void setBounds(android.graphics.Rect);
+ method public void setCheckable(boolean);
+ method public void setChecked(boolean);
+ method public void setClassName(java.lang.CharSequence);
+ method public void setClickable(boolean);
+ method public void setContentDescription(java.lang.CharSequence);
+ method public void setEnabled(boolean);
+ method public void setFocusable(boolean);
+ method public void setFocused(boolean);
+ method public void setLongClickable(boolean);
+ method public void setPackageName(java.lang.CharSequence);
+ method public void setParent(android.view.View);
+ method public void setPassword(boolean);
+ method public void setSelected(boolean);
+ method public void setSource(android.view.View);
+ method public void setText(java.lang.CharSequence);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+ field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+ field public static final int ACTION_FOCUS = 1; // 0x1
+ field public static final int ACTION_SELECT = 4; // 0x4
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public class AccessibilityRecord {
ctor protected AccessibilityRecord();
- method protected void clear();
method public int getAddedCount();
method public java.lang.CharSequence getBeforeText();
- method public boolean getBooleanProperty(int);
method public java.lang.CharSequence getClassName();
method public java.lang.CharSequence getContentDescription();
method public int getCurrentItemIndex();
@@ -22627,6 +22693,7 @@
method public boolean isEnabled();
method public boolean isFullScreen();
method public boolean isPassword();
+ method public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord);
method public static android.view.accessibility.AccessibilityRecord obtain();
method public void recycle();
method public void setAddedCount(int);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 38cacdd..424b70a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -98,6 +98,8 @@
runStart();
} else if (op.equals("startservice")) {
runStartService();
+ } else if (op.equals("force-stop")) {
+ runForceStop();
} else if (op.equals("instrument")) {
runInstrument();
} else if (op.equals("broadcast")) {
@@ -365,6 +367,10 @@
}
}
+ private void runForceStop() throws Exception {
+ mAm.forceStopPackage(nextArgRequired());
+ }
+
private void sendBroadcast() throws Exception {
Intent intent = makeIntent();
IntentReceiver receiver = new IntentReceiver();
@@ -851,7 +857,7 @@
wm.clearForcedDisplaySize();
}
} catch (RemoteException e) {
- }
+ }
}
private class IntentReceiver extends IIntentReceiver.Stub {
@@ -1013,6 +1019,8 @@
"\n" +
" start a Service: am startservice <INTENT>\n" +
"\n" +
+ " force stop everything associated with a package: force-stop <package>\n" +
+ "\n" +
" send a broadcast Intent: am broadcast <INTENT>\n" +
"\n" +
" start an Instrumentation: am instrument [flags] <COMPONENT>\n" +
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 28fc21a..8bb305d 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -25,6 +25,7 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
* An accessibility service runs in the background and receives callbacks by the system
@@ -51,7 +52,11 @@
* enabling or disabling it in the device settings. After the system binds to a service it
* calls {@link AccessibilityService#onServiceConnected()}. This method can be
* overriden by clients that want to perform post binding setup.
+ * </p>
* <p>
+ * An accessibility service can be configured to receive specific types of accessibility events,
+ * listen only to specific packages, get events from each type only once in a given time frame,
+ * retrieve window content, specify a settings activity, etc.
* </p>
* There are two approaches for configuring an accessibility service:
* <ul>
@@ -59,20 +64,23 @@
* Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
* the service. A service declaration with a meta-data tag is presented below:
* <p>
- * <code>
- * <service android:name=".MyAccessibilityService"><br>
- * <intent-filter><br>
- * <action android:name="android.accessibilityservice.AccessibilityService" /><br>
- * </intent-filter><br>
- * <meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /><br>
- * </service><br>
- * </code>
+ * <code>
+ * <service android:name=".MyAccessibilityService"><br>
+ * <intent-filter><br>
+ * <action android:name="android.accessibilityservice.AccessibilityService" /><br>
+ * </intent-filter><br>
+ * <meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /><br>
+ * </service><br>
+ * </code>
* </p>
* <p>
* <strong>
* This approach enables setting all accessibility service properties.
* </strong>
* </p>
+ * <p>
+ * For more details refer to {@link #SERVICE_META_DATA}.
+ * </p>
* </li>
* <li>
* Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
@@ -88,6 +96,9 @@
* {@link AccessibilityServiceInfo#packageNames}
* </strong>
* </p>
+ * <p>
+ * For more details refer to {@link AccessibilityServiceInfo}.
+ * </p>
* </li>
* </ul>
* <p>
@@ -151,16 +162,49 @@
* <code>
* <?xml version="1.0" encoding="utf-8"?><br>
* <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"<br>
- * android:eventTypes="typeViewClicked|typeViewFocused"<br>
+ * android:accessibilityEventTypes="typeViewClicked|typeViewFocused"<br>
* android:packageNames="foo.bar, foo.baz"<br>
- * android:feedbackType="feedbackSpoken"<br>
+ * android:accessibilityFeedbackType="feedbackSpoken"<br>
* android:notificationTimeout="100"<br>
- * android:flags="flagDefault"<br>
+ * android:accessibilityFlags="flagDefault"<br>
* android:settingsActivity="foo.bar.TestBackActivity"<br>
* . . .<br>
* />
* </code>
* </p>
+ * <p>
+ * <strong>Note:</strong> A service can retrieve only the content of the active window.
+ * An active window is the source of the most recent event of type
+ * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START},
+ * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END},
+ * {@link AccessibilityEvent#TYPE_VIEW_CLICKED},
+ * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED},
+ * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
+ * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
+ * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED},
+ * {@link AccessibilityEvent#TYPE_VIEW_SELECTED},
+ * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED},
+ * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}.
+ * Therefore the service should:
+ * <ul>
+ * <li>
+ * Register for all event types with no notification timeout and keep track
+ * for the active window by calling
+ * {@link AccessibilityEvent#getAccessibilityWindowId()} of the last received
+ * event and compare this with the
+ * {@link AccessibilityNodeInfo#getAccessibilityWindowId()} before calling
+ * retrieval methods on the latter.
+ * </li>
+ * <li>
+ * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail
+ * since the active window has changed and the service did not get the
+ * accessibility event. Note that it is possible to have a retrieval method
+ * failing event adopting the strategy specified in the previous bullet
+ * because the accessibility event dispatch is asynchronous and crosses
+ * process boundaries.
+ * </li>
+ * <ul>
+ * </p>
*/
public static final String SERVICE_META_DATA = "android.accessibilityservice";
@@ -224,7 +268,7 @@
/**
* Implement to return the implementation of the internal accessibility
- * service interface. Subclasses should not override.
+ * service interface.
*/
@Override
public final IBinder onBind(Intent intent) {
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 7157def..19f0bf0 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -17,14 +17,72 @@
package android.accessibilityservice;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
- * Interface AccessibilityManagerService#Service implements, and passes to an
- * AccessibilityService so it can dynamically configure how the system handles it.
+ * Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
*
* @hide
*/
-oneway interface IAccessibilityServiceConnection {
+interface IAccessibilityServiceConnection {
void setServiceInfo(in AccessibilityServiceInfo info);
+
+ /**
+ * Finds an {@link AccessibilityNodeInfo} by accessibility id.
+ * <p>
+ * <strong>
+ * It is a client responsibility to recycle the received info by
+ * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+ * of multiple instances.
+ * </strong>
+ * </p>
+ *
+ * @param accessibilityWindowId A unique window id.
+ * @param accessibilityViewId A unique View accessibility id.
+ * @return The node info.
+ */
+ AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+ int accessibilityViewId);
+
+ /**
+ * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
+ * insensitive containment.
+ * <p>
+ * <strong>
+ * It is a client responsibility to recycle the received infos by
+ * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+ * of multiple instances.
+ * </strong>
+ * </p>
+ *
+ * @param text The searched text.
+ * @return A list of node info.
+ */
+ List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text);
+
+ /**
+ * Finds an {@link AccessibilityNodeInfo} by View id.
+ * <p>
+ * <strong>
+ * It is a client responsibility to recycle the received info by
+ * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+ * of multiple instances.
+ * </strong>
+ * </p>
+ *
+ * @param id The id of the node.
+ * @return The node info.
+ */
+ AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId);
+
+ /**
+ * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
+ *
+ * @param accessibilityWindowId The id of the window.
+ * @param accessibilityViewId The of a view in the .
+ * @return Whether the action was performed.
+ */
+ boolean performAccessibilityAction(int accessibilityWindowId, int accessibilityViewId,
+ int action);
}
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 17f8adb..63f3258 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -278,7 +278,6 @@
int token, IBackupManager callbackBinder) throws RemoteException {
long ident = Binder.clearCallingIdentity();
try {
-Log.d(TAG, "doRestoreFile() => onRestoreFile()");
BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
} catch (IOException e) {
throw new RuntimeException(e);
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index dfb0dd7..3b70e19 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -46,7 +46,7 @@
public static final String SHARED_STORAGE_TOKEN = "shared";
public static final String APPS_PREFIX = "apps/";
- public static final String SHARED_PREFIX = "shared/";
+ public static final String SHARED_PREFIX = SHARED_STORAGE_TOKEN + "/";
public static final String FULL_BACKUP_INTENT_ACTION = "fullback";
public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
@@ -61,7 +61,8 @@
String linkdomain, String rootpath, String path, BackupDataOutput output);
static public void restoreToFile(ParcelFileDescriptor data,
- long size, int type, long mode, long mtime, File outFile) throws IOException {
+ long size, int type, long mode, long mtime, File outFile,
+ boolean doChmod) throws IOException {
if (type == FullBackup.TYPE_DIRECTORY) {
// Canonically a directory has no associated content, so we don't need to read
// anything from the pipe in this case. Just create the directory here and
@@ -116,7 +117,7 @@
}
// Now twiddle the state to match the backup, assuming all went well
- if (outFile != null) {
+ if (doChmod && outFile != null) {
try {
Libcore.os.chmod(outFile.getPath(), (int)mode);
} catch (ErrnoException e) {
diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java
index 4dca593..df1c363 100644
--- a/core/java/android/app/backup/FullBackupAgent.java
+++ b/core/java/android/app/backup/FullBackupAgent.java
@@ -28,8 +28,6 @@
import libcore.io.StructStat;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
@@ -84,9 +82,10 @@
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) {
+ ParcelFileDescriptor newState) throws IOException {
// Filters, the scan queue, and the set of resulting entities
HashSet<String> filterSet = new HashSet<String>();
+ String packageName = getPackageName();
// Okay, start with the app's root tree, but exclude all of the canonical subdirs
if (mLibDir != null) {
@@ -96,25 +95,28 @@
filterSet.add(mDatabaseDir);
filterSet.add(mSharedPrefsDir);
filterSet.add(mFilesDir);
- processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
+ processTree(packageName, FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
// Now do the same for the files dir, db dir, and shared prefs dir
filterSet.add(mMainDir);
filterSet.remove(mFilesDir);
- processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
+ processTree(packageName, FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
filterSet.add(mFilesDir);
filterSet.remove(mDatabaseDir);
- processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
+ processTree(packageName, FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
filterSet.add(mDatabaseDir);
filterSet.remove(mSharedPrefsDir);
- processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
+ processTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
}
- private void processTree(String domain, String rootPath,
+ // Scan the dir tree (if it actually exists) and process each entry we find. If the
+ // 'excludes' parameter is non-null, it is consulted each time a new file system entity
+ // is visited to see whether that entity (and its subtree, if appropriate) should be
+ // omitted from the backup process.
+ protected void processTree(String packageName, String domain, String rootPath,
HashSet<String> excludes, BackupDataOutput data) {
- // Scan the dir tree (if it actually exists) and process each entry we find
File rootFile = new File(rootPath);
if (rootFile.exists()) {
LinkedList<File> scanQueue = new LinkedList<File>();
@@ -125,7 +127,7 @@
String filePath = file.getAbsolutePath();
// prune this subtree?
- if (excludes.contains(filePath)) {
+ if (excludes != null && excludes.contains(filePath)) {
continue;
}
@@ -149,7 +151,7 @@
}
// Finally, back this file up before proceeding
- FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data);
+ FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, data);
}
}
}
@@ -218,6 +220,6 @@
if (DEBUG) Log.i(TAG, "[" + domain + " : " + relpath + "] mapped to " + outFile.getPath());
// Now that we've figured out where the data goes, send it on its way
- FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+ FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
}
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 26707c9..a8c31f9 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -873,10 +873,10 @@
/**
* Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
- * <p>The link key will be unauthenticated i.e the communication is
+ * <p>The link key is not required to be authenticated, i.e the communication may be
* vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices,
- * the link key will be encrypted, as encryption is mandartory.
- * For legacy devices (pre Bluetooth 2.1 devices) the link key will not
+ * the link will be encrypted, as encryption is mandartory.
+ * For legacy devices (pre Bluetooth 2.1 devices) the link will not
* be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
* encrypted and authenticated communication channel is desired.
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
@@ -902,6 +902,44 @@
return createNewRfcommSocketAndRecord(name, uuid, false, false);
}
+ /**
+ * Create a listening, encrypted,
+ * RFCOMM Bluetooth socket with Service Record.
+ * <p>The link will be encrypted, but the link key is not required to be authenticated
+ * i.e the communication is vulnerable to Man In the Middle attacks. Use
+ * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key.
+ * <p> Use this socket if authentication of link key is not possible.
+ * For example, for Bluetooth 2.1 devices, if any of the devices does not have
+ * an input and output capability or just has the ability to display a numeric key,
+ * a secure socket connection is not possible and this socket can be used.
+ * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required.
+ * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandartory.
+ * For more details, refer to the Security Model section 5.2 (vol 3) of
+ * Bluetooth Core Specification version 2.1 + EDR.
+ * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
+ * connections from a listening {@link BluetoothServerSocket}.
+ * <p>The system will assign an unused RFCOMM channel to listen on.
+ * <p>The system will also register a Service Discovery
+ * Protocol (SDP) record with the local SDP server containing the specified
+ * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
+ * can use the same UUID to query our SDP server and discover which channel
+ * to connect to. This SDP record will be removed when this socket is
+ * closed, or if this application closes unexpectedly.
+ * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
+ * connect to this socket from another device using the same {@link UUID}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ * @param name service name for SDP record
+ * @param uuid uuid for SDP record
+ * @return a listening RFCOMM BluetoothServerSocket
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions, or channel in use.
+ * @hide
+ */
+ public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(
+ String name, UUID uuid) throws IOException {
+ return createNewRfcommSocketAndRecord(name, uuid, false, true);
+ }
+
private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
boolean auth, boolean encrypt) throws IOException {
RfcommChannelPicker picker = new RfcommChannelPicker(uuid);
@@ -973,6 +1011,28 @@
return socket;
}
+ /**
+ * Construct an encrypted, RFCOMM server socket.
+ * Call #accept to retrieve connections to this socket.
+ * @return An RFCOMM BluetoothServerSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port)
+ throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, false, true, port);
+ int errno = socket.mSocket.bindListen();
+ if (errno != 0) {
+ try {
+ socket.close();
+ } catch (IOException e) {}
+ socket.mSocket.throwErrnoNative(errno);
+ }
+ return socket;
+ }
+
/**
* Construct a SCO server socket.
* Call #accept to retrieve connections to this socket.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3d637e9..2f9627a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1160,6 +1160,15 @@
public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
/**
+ * Activity Action: Show settings for managing network data usage of a
+ * specific application. Applications should define an activity that offers
+ * options to control data usage.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_NETWORK_USAGE =
+ "android.intent.action.MANAGE_NETWORK_USAGE";
+
+ /**
* A string associated with a {@link #ACTION_UPGRADE_SETUP} activity
* describing the last run version of the platform that was setup.
* @hide
@@ -1654,8 +1663,9 @@
* This is used mainly for the USB Settings panel.
* Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified
* when the SD card file system is mounted or unmounted
+ * @deprecated replaced by android.os.storage.StorageEventListener
*/
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
/**
@@ -1663,8 +1673,9 @@
* This is used mainly for the USB Settings panel.
* Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified
* when the SD card file system is mounted or unmounted
+ * @deprecated replaced by android.os.storage.StorageEventListener
*/
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 09fede0..31ad6e9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1517,11 +1517,12 @@
}
}
+ // fullBackupAgent is explicitly handled even if allowBackup is false
name = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_fullBackupAgent, 0);
if (name != null) {
ai.fullBackupAgentName = buildClassName(pkgName, name, outError);
- if (true) {
+ if (false) {
Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName
+ " from " + pkgName + "+" + name);
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index d5c4ace..dea5133 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -16,22 +16,22 @@
package android.hardware;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.StringTokenizer;
-import java.io.IOException;
-
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.StringTokenizer;
/**
* The Camera class is used to set image capture settings, start/stop preview,
@@ -834,8 +834,6 @@
* @param raw the callback for raw (uncompressed) image data, or null
* @param postview callback with postview image data, may be null
* @param jpeg the callback for JPEG image data, or null
- *
- * @see #addRawImageCallbackBuffer(byte[])
*/
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback postview, PictureCallback jpeg) {
@@ -1084,10 +1082,14 @@
};
/**
- * Area class for focus.
+ * Area class for focus and metering.
*
- * @see #setFocusAreas(List)
- * @see #getFocusAreas()
+ * @see Parameters#setFocusAreas(List)
+ * @see Parameters#getFocusAreas()
+ * @see Parameters#getMaxNumFocusAreas()
+ * @see Parameters#setMeteringAreas(List)
+ * @see Parameters#getMeteringAreas()
+ * @see Parameters#getMaxNumMeteringAreas()
*/
public static class Area {
/**
@@ -1121,12 +1123,22 @@
return weight == a.weight;
}
- /** rectangle of the area */
+ /**
+ * Rectangle of the area.
+ *
+ * @see Parameters#getFocusAreas()
+ * @see Parameters#getMeteringAreas()
+ */
public Rect rect;
- /** weight of the area */
+ /**
+ * Weight of the area.
+ *
+ * @see Parameters#getFocusAreas()
+ * @see Parameters#getMeteringAreas()
+ */
public int weight;
- };
+ }
/**
* Camera service settings.
@@ -2775,7 +2787,7 @@
* The direction is not affected by the rotation or mirroring of
* {@link #setDisplayOrientation(int)}. Coordinates of the rectangle
* range from -1000 to 1000. (-1000, -1000) is the upper left point.
- * (1000, 1000) is the lower right point. The length and width of focus
+ * (1000, 1000) is the lower right point. The width and height of focus
* areas cannot be 0 or negative.
*
* The weight must range from 1 to 1000. The weight should be
@@ -2842,7 +2854,7 @@
* sensor sees. The direction is not affected by the rotation or
* mirroring of {@link #setDisplayOrientation(int)}. Coordinates of the
* rectangle range from -1000 to 1000. (-1000, -1000) is the upper left
- * point. (1000, 1000) is the lower right point. The length and width of
+ * point. (1000, 1000) is the lower right point. The width and height of
* metering areas cannot be 0 or negative.
*
* The weight must range from 1 to 1000, and represents a weight for
@@ -3033,7 +3045,7 @@
if (result.size() == 0) return null;
if (result.size() == 1) {
- Area area = (Area) result.get(0);
+ Area area = result.get(0);
Rect rect = area.rect;
if (rect.left == 0 && rect.top == 0 && rect.right == 0
&& rect.bottom == 0 && area.weight == 0) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c72c4b0..21cce44 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -256,10 +256,61 @@
private final IConnectivityManager mService;
- static public boolean isNetworkTypeValid(int networkType) {
+ public static boolean isNetworkTypeValid(int networkType) {
return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
}
+ /** {@hide} */
+ public static String getNetworkTypeName(int type) {
+ switch (type) {
+ case TYPE_MOBILE:
+ return "MOBILE";
+ case TYPE_WIFI:
+ return "WIFI";
+ case TYPE_MOBILE_MMS:
+ return "MOBILE_MMS";
+ case TYPE_MOBILE_SUPL:
+ return "MOBILE_SUPL";
+ case TYPE_MOBILE_DUN:
+ return "MOBILE_DUN";
+ case TYPE_MOBILE_HIPRI:
+ return "MOBILE_HIPRI";
+ case TYPE_WIMAX:
+ return "WIMAX";
+ case TYPE_BLUETOOTH:
+ return "BLUETOOTH";
+ case TYPE_DUMMY:
+ return "DUMMY";
+ case TYPE_ETHERNET:
+ return "ETHERNET";
+ case TYPE_MOBILE_FOTA:
+ return "MOBILE_FOTA";
+ case TYPE_MOBILE_IMS:
+ return "MOBILE_IMS";
+ case TYPE_MOBILE_CBS:
+ return "MOBILE_CBS";
+ default:
+ return Integer.toString(type);
+ }
+ }
+
+ /** {@hide} */
+ public static boolean isNetworkTypeMobile(int networkType) {
+ switch (networkType) {
+ case TYPE_MOBILE:
+ case TYPE_MOBILE_MMS:
+ case TYPE_MOBILE_SUPL:
+ case TYPE_MOBILE_DUN:
+ case TYPE_MOBILE_HIPRI:
+ case TYPE_MOBILE_FOTA:
+ case TYPE_MOBILE_IMS:
+ case TYPE_MOBILE_CBS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
public void setNetworkPreference(int preference) {
try {
mService.setNetworkPreference(preference);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 647a60a..07f6cece 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@
import android.net.LinkProperties;
import android.net.NetworkInfo;
+import android.net.NetworkState;
import android.net.ProxyProperties;
import android.os.IBinder;
@@ -40,6 +41,8 @@
LinkProperties getActiveLinkProperties();
LinkProperties getLinkProperties(int networkType);
+ NetworkState[] getAllNetworkState();
+
boolean setRadios(boolean onOff);
boolean setRadio(int networkType, boolean turnOn);
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 6d57036..d38d16c 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -16,12 +16,18 @@
package android.net;
+import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
/** {@hide} */
interface INetworkStatsService {
- NetworkStatsHistory[] getNetworkStatsSummary(int networkType);
- NetworkStatsHistory getNetworkStatsUid(int uid);
+ /** Return historical stats for traffic that matches template. */
+ NetworkStatsHistory getHistoryForNetwork(int networkTemplate);
+ /** Return historical stats for specific UID traffic that matches template. */
+ NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate);
+
+ /** Return usage summary per UID for traffic that matches template. */
+ NetworkStats getSummaryPerUid(long start, long end, int networkTemplate);
}
diff --git a/core/java/android/net/NetworkState.aidl b/core/java/android/net/NetworkState.aidl
new file mode 100644
index 0000000..c0b6cdc
--- /dev/null
+++ b/core/java/android/net/NetworkState.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+parcelable NetworkState;
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
new file mode 100644
index 0000000..749039a
--- /dev/null
+++ b/core/java/android/net/NetworkState.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Snapshot of network state.
+ *
+ * @hide
+ */
+public class NetworkState implements Parcelable {
+
+ public final NetworkInfo networkInfo;
+ public final LinkProperties linkProperties;
+ public final LinkCapabilities linkCapabilities;
+
+ public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
+ LinkCapabilities linkCapabilities) {
+ this.networkInfo = networkInfo;
+ this.linkProperties = linkProperties;
+ this.linkCapabilities = linkCapabilities;
+ }
+
+ public NetworkState(Parcel in) {
+ networkInfo = in.readParcelable(null);
+ linkProperties = in.readParcelable(null);
+ linkCapabilities = in.readParcelable(null);
+ }
+
+ /** {@inheritDoc} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(networkInfo, flags);
+ out.writeParcelable(linkProperties, flags);
+ out.writeParcelable(linkCapabilities, flags);
+ }
+
+ public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
+ public NetworkState createFromParcel(Parcel in) {
+ return new NetworkState(in);
+ }
+
+ public NetworkState[] newArray(int size) {
+ return new NetworkState[size];
+ }
+ };
+
+}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 588bf64..ee415fa 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -19,6 +19,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
+import android.util.SparseBooleanArray;
import java.io.CharArrayWriter;
import java.io.PrintWriter;
@@ -125,7 +126,7 @@
/**
* Return list of unique interfaces known by this data structure.
*/
- public String[] getKnownIfaces() {
+ public String[] getUniqueIfaces() {
final HashSet<String> ifaces = new HashSet<String>();
for (String iface : this.iface) {
if (iface != IFACE_ALL) {
@@ -136,6 +137,23 @@
}
/**
+ * Return list of unique UIDs known by this data structure.
+ */
+ public int[] getUniqueUids() {
+ final SparseBooleanArray uids = new SparseBooleanArray();
+ for (int uid : this.uid) {
+ uids.put(uid, true);
+ }
+
+ final int size = uids.size();
+ final int[] result = new int[size];
+ for (int i = 0; i < size; i++) {
+ result[i] = uids.keyAt(i);
+ }
+ return result;
+ }
+
+ /**
* Subtract the given {@link NetworkStats}, effectively leaving the delta
* between two snapshots in time. Assumes that statistics rows collect over
* time, and that none of them have disappeared.
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index b16101f..5edbf58 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -24,7 +24,9 @@
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.net.ProtocolException;
import java.util.Arrays;
+import java.util.Random;
/**
* Collection of historical network statistics, recorded into equally-sized
@@ -38,28 +40,19 @@
* @hide
*/
public class NetworkStatsHistory implements Parcelable {
- private static final int VERSION = 1;
-
- /** {@link #uid} value when UID details unavailable. */
- public static final int UID_ALL = -1;
+ private static final int VERSION_CURRENT = 1;
// TODO: teach about zigzag encoding to use less disk space
// TODO: teach how to convert between bucket sizes
- public final int networkType;
- public final String identity;
- public final int uid;
public final long bucketDuration;
- int bucketCount;
- long[] bucketStart;
- long[] rx;
- long[] tx;
+ public int bucketCount;
+ public long[] bucketStart;
+ public long[] rx;
+ public long[] tx;
- public NetworkStatsHistory(int networkType, String identity, int uid, long bucketDuration) {
- this.networkType = networkType;
- this.identity = identity;
- this.uid = uid;
+ public NetworkStatsHistory(long bucketDuration) {
this.bucketDuration = bucketDuration;
bucketStart = new long[0];
rx = new long[0];
@@ -68,9 +61,6 @@
}
public NetworkStatsHistory(Parcel in) {
- networkType = in.readInt();
- identity = in.readString();
- uid = in.readInt();
bucketDuration = in.readLong();
bucketStart = readLongArray(in);
rx = in.createLongArray();
@@ -80,9 +70,6 @@
/** {@inheritDoc} */
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(networkType);
- out.writeString(identity);
- out.writeInt(uid);
out.writeLong(bucketDuration);
writeLongArray(out, bucketStart, bucketCount);
writeLongArray(out, rx, bucketCount);
@@ -91,21 +78,23 @@
public NetworkStatsHistory(DataInputStream in) throws IOException {
final int version = in.readInt();
- networkType = in.readInt();
- identity = in.readUTF();
- uid = in.readInt();
- bucketDuration = in.readLong();
- bucketStart = readLongArray(in);
- rx = readLongArray(in);
- tx = readLongArray(in);
- bucketCount = bucketStart.length;
+ switch (version) {
+ case VERSION_CURRENT: {
+ bucketDuration = in.readLong();
+ bucketStart = readLongArray(in);
+ rx = readLongArray(in);
+ tx = readLongArray(in);
+ bucketCount = bucketStart.length;
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
}
public void writeToStream(DataOutputStream out) throws IOException {
- out.writeInt(VERSION);
- out.writeInt(networkType);
- out.writeUTF(identity);
- out.writeInt(uid);
+ out.writeInt(VERSION_CURRENT);
out.writeLong(bucketDuration);
writeLongArray(out, bucketStart, bucketCount);
writeLongArray(out, rx, bucketCount);
@@ -145,6 +134,18 @@
}
/**
+ * Record an entire {@link NetworkStatsHistory} into this history. Usually
+ * for combining together stats for external reporting.
+ */
+ public void recordEntireHistory(NetworkStatsHistory input) {
+ for (int i = 0; i < input.bucketCount; i++) {
+ final long start = input.bucketStart[i];
+ final long end = start + input.bucketDuration;
+ recordData(start, end, input.rx[i], input.tx[i]);
+ }
+ }
+
+ /**
* Ensure that buckets exist for given time range, creating as needed.
*/
private void ensureBuckets(long start, long end) {
@@ -213,15 +214,65 @@
}
}
+ /**
+ * Return interpolated data usage across the requested range. Interpolates
+ * across buckets, so values may be rounded slightly.
+ */
+ public void getTotalData(long start, long end, long[] outTotal) {
+ long rx = 0;
+ long tx = 0;
+
+ for (int i = bucketCount - 1; i >= 0; i--) {
+ final long curStart = bucketStart[i];
+ final long curEnd = curStart + bucketDuration;
+
+ // bucket is older than record; we're finished
+ if (curEnd < start) break;
+ // bucket is newer than record; keep looking
+ if (curStart > end) continue;
+
+ final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
+ if (overlap > 0) {
+ rx += this.rx[i] * overlap / bucketDuration;
+ tx += this.tx[i] * overlap / bucketDuration;
+ }
+ }
+
+ outTotal[0] = rx;
+ outTotal[1] = tx;
+ }
+
+ /**
+ * @deprecated only for temporary testing
+ */
+ @Deprecated
+ public void generateRandom(long start, long end, long rx, long tx) {
+ ensureBuckets(start, end);
+
+ final Random r = new Random();
+ while (rx > 1024 && tx > 1024) {
+ final long curStart = randomLong(r, start, end);
+ final long curEnd = randomLong(r, curStart, end);
+ final long curRx = randomLong(r, 0, rx);
+ final long curTx = randomLong(r, 0, tx);
+
+ recordData(curStart, curEnd, curRx, curTx);
+
+ rx -= curRx;
+ tx -= curTx;
+ }
+ }
+
+ private static long randomLong(Random r, long start, long end) {
+ return (long) (start + (r.nextFloat() * (end - start)));
+ }
+
public void dump(String prefix, PrintWriter pw) {
- // TODO: consider stripping identity when dumping
pw.print(prefix);
- pw.print("NetworkStatsHistory: networkType="); pw.print(networkType);
- pw.print(" identity="); pw.print(identity);
- pw.print(" uid="); pw.println(uid);
+ pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration);
for (int i = 0; i < bucketCount; i++) {
pw.print(prefix);
- pw.print(" timestamp="); pw.print(bucketStart[i]);
+ pw.print(" bucketStart="); pw.print(bucketStart[i]);
pw.print(" rx="); pw.print(rx[i]);
pw.print(" tx="); pw.println(tx[i]);
}
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 3bf64b2..8e50cd5 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -28,6 +28,7 @@
import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
@@ -86,6 +87,8 @@
private SSLSocketFactory mInsecureFactory = null;
private SSLSocketFactory mSecureFactory = null;
+ private TrustManager[] mTrustManagers = null;
+ private KeyManager[] mKeyManagers = null;
private final int mHandshakeTimeoutMillis;
private final SSLClientSessionCache mSessionCache;
@@ -197,10 +200,11 @@
}
}
- private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) {
+ private SSLSocketFactory makeSocketFactory(
+ KeyManager[] keyManagers, TrustManager[] trustManagers) {
try {
OpenSSLContextImpl sslContext = new OpenSSLContextImpl();
- sslContext.engineInit(null, trustManagers, null);
+ sslContext.engineInit(keyManagers, trustManagers, null);
sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache);
return sslContext.engineGetSocketFactory();
} catch (KeyManagementException e) {
@@ -223,18 +227,44 @@
} else {
Log.w(TAG, "Bypassing SSL security checks at caller's request");
}
- mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER);
+ mInsecureFactory = makeSocketFactory(mKeyManagers, INSECURE_TRUST_MANAGER);
}
return mInsecureFactory;
} else {
if (mSecureFactory == null) {
- mSecureFactory = makeSocketFactory(null);
+ mSecureFactory = makeSocketFactory(mKeyManagers, mTrustManagers);
}
return mSecureFactory;
}
}
/**
+ * Sets the {@link TrustManager}s to be used for connections made by this factory.
+ * @hide
+ */
+ public void setTrustManagers(TrustManager[] trustManager) {
+ mTrustManagers = trustManager;
+
+ // Clear out all cached secure factories since configurations have changed.
+ mSecureFactory = null;
+ // Note - insecure factories only ever use the INSECURE_TRUST_MANAGER so they need not
+ // be cleared out here.
+ }
+
+ /**
+ * Sets the {@link KeyManager}s to be used for connections made by this factory.
+ * @hide
+ */
+ public void setKeyManagers(KeyManager[] keyManagers) {
+ mKeyManagers = keyManagers;
+
+ // Clear out any existing cached factories since configurations have changed.
+ mSecureFactory = null;
+ mInsecureFactory = null;
+ }
+
+
+ /**
* {@inheritDoc}
*
* <p>This method verifies the peer's certificate hostname after connecting
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 8ab64fa..a0738c1 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -41,6 +41,42 @@
*/
public final static int UNSUPPORTED = -1;
+ // TODO: find better home for these template constants
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+ * networks together. Only uses statistics for currently active IMSI.
+ *
+ * @hide
+ */
+ public static final int TEMPLATE_MOBILE_ALL = 1;
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+ * networks together that roughly meet a "3G" definition, or lower. Only
+ * uses statistics for currently active IMSI.
+ *
+ * @hide
+ */
+ public static final int TEMPLATE_MOBILE_3G_LOWER = 2;
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+ * networks together that meet a "4G" definition. Only uses statistics for
+ * currently active IMSI.
+ *
+ * @hide
+ */
+ public static final int TEMPLATE_MOBILE_4G = 3;
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style
+ * networks together.
+ *
+ * @hide
+ */
+ public static final int TEMPLATE_WIFI = 4;
+
/**
* Snapshot of {@link NetworkStats} when the currently active profiling
* session started, or {@code null} if no session active.
diff --git a/core/java/android/nfc/ErrorCodes.java b/core/java/android/nfc/ErrorCodes.java
index 69329df..3adcdc3 100644
--- a/core/java/android/nfc/ErrorCodes.java
+++ b/core/java/android/nfc/ErrorCodes.java
@@ -57,6 +57,7 @@
case ERROR_SE_ALREADY_SELECTED: return "SE_ALREADY_SELECTED";
case ERROR_SE_CONNECTED: return "SE_CONNECTED";
case ERROR_NO_SE_CONNECTED: return "NO_SE_CONNECTED";
+ case ERROR_NOT_SUPPORTED: return "NOT_SUPPORTED";
default: return "UNKNOWN ERROR";
}
}
@@ -105,4 +106,6 @@
public static final int ERROR_NO_SE_CONNECTED = -20;
-}
\ No newline at end of file
+ public static final int ERROR_NOT_SUPPORTED = -21;
+
+}
diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java
index 7ec807a..6557ee0 100644
--- a/core/java/android/nfc/tech/BasicTagTechnology.java
+++ b/core/java/android/nfc/tech/BasicTagTechnology.java
@@ -77,6 +77,10 @@
// Store this in the tag object
mTag.setConnectedTechnology(mSelectedTechnology);
mIsConnected = true;
+ } else if (errorCode == ErrorCodes.ERROR_NOT_SUPPORTED) {
+ throw new UnsupportedOperationException("Connecting to " +
+ "this technology is not supported by the NFC " +
+ "adapter.");
} else {
throw new IOException();
}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index bc4208a..792e4c1 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -32,6 +32,7 @@
private final boolean mRemovable;
private final boolean mEmulated;
private final int mMtpReserveSpace;
+ private final boolean mAllowMassStorage;
private int mStorageId;
// StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
@@ -39,23 +40,25 @@
// ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
- public StorageVolume(String path, String description,
- boolean removable, boolean emulated, int mtpReserveSpace) {
+ public StorageVolume(String path, String description, boolean removable,
+ boolean emulated, int mtpReserveSpace, boolean allowMassStorage) {
mPath = path;
mDescription = description;
mRemovable = removable;
mEmulated = emulated;
mMtpReserveSpace = mtpReserveSpace;
+ mAllowMassStorage = allowMassStorage;
}
// for parcelling only
- private StorageVolume(String path, String description,
- boolean removable, boolean emulated, int mtpReserveSpace, int storageId) {
+ private StorageVolume(String path, String description, boolean removable,
+ boolean emulated, int mtpReserveSpace, int storageId, boolean allowMassStorage) {
mPath = path;
mDescription = description;
mRemovable = removable;
mEmulated = emulated;
mMtpReserveSpace = mtpReserveSpace;
+ mAllowMassStorage = allowMassStorage;
mStorageId = storageId;
}
@@ -130,6 +133,15 @@
return mMtpReserveSpace;
}
+ /**
+ * Returns true if this volume can be shared via USB mass storage.
+ *
+ * @return whether mass storage is allowed
+ */
+ public boolean allowMassStorage() {
+ return mAllowMassStorage;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj instanceof StorageVolume && mPath != null) {
@@ -158,9 +170,10 @@
int emulated = in.readInt();
int storageId = in.readInt();
int mtpReserveSpace = in.readInt();
+ int allowMassStorage = in.readInt();
return new StorageVolume(path, description,
removable == 1, emulated == 1,
- mtpReserveSpace, storageId);
+ mtpReserveSpace, storageId, allowMassStorage == 1);
}
public StorageVolume[] newArray(int size) {
@@ -179,5 +192,6 @@
parcel.writeInt(mEmulated ? 1 : 0);
parcel.writeInt(mStorageId);
parcel.writeInt(mMtpReserveSpace);
+ parcel.writeInt(mAllowMassStorage ? 1 : 0);
}
}
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 0ef38bf..a06d0f6 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -16,6 +16,7 @@
package android.provider;
+
import android.accounts.Account;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -32,7 +33,6 @@
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.RemoteException;
-import android.pim.ICalendar;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
@@ -44,24 +44,30 @@
* @hide
*/
public final class Calendar {
-
- public static final String TAG = "Calendar";
+ private static final String TAG = "Calendar";
/**
- * Broadcast Action: An event reminder.
+ * Broadcast Action: This is the intent that gets fired when an alarm
+ * notification needs to be posted for a reminder.
*/
public static final String EVENT_REMINDER_ACTION = "android.intent.action.EVENT_REMINDER";
/**
- * These are the symbolic names for the keys used in the extra data
- * passed in the intent for event reminders.
+ * Intent Extras key: The start time of an event or an instance of a
+ * recurring event. (milliseconds since epoch)
*/
public static final String EVENT_BEGIN_TIME = "beginTime";
+
+ /**
+ * Intent Extras key: The end time of an event or an instance of a recurring
+ * event. (milliseconds since epoch)
+ */
public static final String EVENT_END_TIME = "endTime";
/**
- * This must not be changed or horrible, unspeakable things could happen.
- * For instance, the Calendar app might break. Also, the db might not work.
+ * This authority is used for writing to or querying from the calendar
+ * provider. Note: This is set at first run and cannot be changed without
+ * breaking apps that access the provider.
*/
public static final String AUTHORITY = "com.android.calendar";
@@ -73,20 +79,37 @@
/**
* An optional insert, update or delete URI parameter that allows the caller
- * to specify that it is a sync adapter. The default value is false. If true
- * the dirty flag is not automatically set and the "syncToNetwork" parameter
- * is set to false when calling
- * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
+ * to specify that it is a sync adapter. The default value is false. If set
+ * to true, the modified row is not marked as "dirty" (needs to be synced)
+ * and when the provider calls
+ * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
+ * , the third parameter "syncToNetwork" is set to false. Furthermore, if
+ * set to true, the caller must also include
+ * {@link SyncColumns#ACCOUNT_NAME} and {@link SyncColumns#ACCOUNT_TYPE} as
+ * query parameters.
+ *
+ * @See Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String)
*/
public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
+ /**
+ * A special account type for calendars not associated with any account.
+ * Normally calendars that do not match an account on the device will be
+ * removed. Setting the account_type on a calendar to this will prevent it
+ * from being wiped if it does not match an existing account.
+ *
+ * @see SyncColumns#ACCOUNT_TYPE
+ */
+ public static final String ACCOUNT_TYPE_LOCAL = "LOCAL";
/**
- * Generic columns for use by sync adapters. The specific functions of
- * these columns are private to the sync adapter. Other clients of the API
- * should not attempt to either read or write this column.
+ * Generic columns for use by sync adapters. The specific functions of these
+ * columns are private to the sync adapter. Other clients of the API should
+ * not attempt to either read or write this column. These columns are
+ * editable as part of the Calendars Uri, but can only be read if accessed
+ * through any other Uri.
*/
- protected interface BaseSyncColumns {
+ protected interface CalendarSyncColumns {
/** Generic column for use by sync adapters. */
public static final String CAL_SYNC1 = "cal_sync1";
@@ -103,29 +126,41 @@
}
/**
- * Columns for Sync information used by Calendars and Events tables.
+ * Columns for Sync information used by Calendars and Events tables. These
+ * have specific uses which are expected to be consistent by the app and
+ * sync adapter.
+ *
+ * @hide
*/
- public interface SyncColumns extends BaseSyncColumns {
+ public interface SyncColumns extends CalendarSyncColumns {
/**
- * The account that was used to sync the entry to the device.
+ * The account that was used to sync the entry to the device. If the
+ * account_type is not {@link #ACCOUNT_TYPE_LOCAL} then the name and
+ * type must match an account on the device or the calendar will be
+ * deleted.
* <P>Type: TEXT</P>
*/
public static final String ACCOUNT_NAME = "account_name";
/**
- * The type of the account that was used to sync the entry to the device.
+ * The type of the account that was used to sync the entry to the
+ * device. A type of {@link #ACCOUNT_TYPE_LOCAL} will keep this event
+ * form being deleted if there are no matching accounts on the device.
* <P>Type: TEXT</P>
*/
public static final String ACCOUNT_TYPE = "account_type";
/**
- * The unique ID for a row assigned by the sync source. NULL if the row has never been synced.
+ * The unique ID for a row assigned by the sync source. NULL if the row
+ * has never been synced. This is used as a reference id for exceptions
+ * along with {@link BaseColumns#_ID}.
* <P>Type: TEXT</P>
*/
public static final String _SYNC_ID = "_sync_id";
/**
- * The last time, from the sync source's point of view, that this row has been synchronized.
+ * The last time, from the sync source's point of view, that this row
+ * has been synchronized.
* <P>Type: INTEGER (long)</P>
*/
public static final String _SYNC_TIME = "_sync_time";
@@ -148,12 +183,21 @@
*/
public static final String DIRTY = "dirty";
+ /**
+ * If set to 1 this causes events on this calendar to be duplicated with
+ * {@link EventsColumns#LAST_SYNCED} set to 1 whenever the event transitions from non-dirty
+ * to dirty. The duplicated event will not be expanded in the instances table and will only
+ * show up in sync adapter queries of the events table. It will also be deleted when the
+ * originating event has its dirty flag cleared by the sync adapter.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String CAN_PARTIALLY_UPDATE = "canPartiallyUpdate";
}
/**
- * Columns from the Calendars table that other tables join into themselves.
+ * Columns specific to the Calendars Uri that other Uris can query.
*/
- public interface CalendarsColumns {
+ private interface CalendarsColumns {
/**
* The color of the calendar
* <P>Type: INTEGER (color value)</P>
@@ -172,10 +216,17 @@
public static final int FREEBUSY_ACCESS = 100;
/** Can read all event details */
public static final int READ_ACCESS = 200;
+ /** Can reply yes/no/maybe to an event */
public static final int RESPOND_ACCESS = 300;
+ /** not used */
public static final int OVERRIDE_ACCESS = 400;
- /** Full access to modify the calendar, but not the access control settings */
+ /** Full access to modify the calendar, but not the access control
+ * settings
+ */
public static final int CONTRIBUTOR_ACCESS = 500;
+ /** Full access to modify the calendar, but not the access control
+ * settings
+ */
public static final int EDITOR_ACCESS = 600;
/** Full access to the calendar */
public static final int OWNER_ACCESS = 700;
@@ -184,31 +235,35 @@
/**
* Is the calendar selected to be displayed?
+ * 0 - do not show events associated with this calendar.
+ * 1 - show events associated with this calendar
* <P>Type: INTEGER (boolean)</P>
*/
public static final String VISIBLE = "visible";
/**
- * The timezone the calendar's events occurs in
+ * The time zone the calendar is associated with.
* <P>Type: TEXT</P>
*/
public static final String CALENDAR_TIMEZONE = "calendar_timezone";
/**
- * If this calendar is in the list of calendars that are selected for
- * syncing then "sync_events" is 1, otherwise 0.
+ * Is this calendar synced and are its events stored on the device?
+ * 0 - Do not sync this calendar or store events for this calendar.
+ * 1 - Sync down events for this calendar.
* <p>Type: INTEGER (boolean)</p>
*/
public static final String SYNC_EVENTS = "sync_events";
/**
- * Sync state data.
+ * Sync state data. Usable by the sync adapter.
* <p>Type: String (blob)</p>
*/
public static final String SYNC_STATE = "sync_state";
/**
- * Whether the row has been deleted. A deleted row should be ignored.
+ * Whether the row has been deleted but not synced to the server. A
+ * deleted row should be ignored.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String DELETED = "deleted";
@@ -216,35 +271,32 @@
/**
* Class that represents a Calendar Entity. There is one entry per calendar.
+ * This is a helper class to make batch operations easier.
*/
public static class CalendarsEntity implements BaseColumns, SyncColumns, CalendarsColumns {
+ /**
+ * The default Uri used when creating a new calendar EntityIterator.
+ */
+ @SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
"/calendar_entities");
- public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
- return new EntityIteratorImpl(cursor, resolver);
- }
-
- public static EntityIterator newEntityIterator(Cursor cursor,
- ContentProviderClient provider) {
- return new EntityIteratorImpl(cursor, provider);
+ /**
+ * Creates an entity iterator for the given cursor. It assumes the
+ * cursor contains a calendars query.
+ *
+ * @param cursor query on {@link #CONTENT_URI}
+ * @return an EntityIterator of calendars
+ */
+ public static EntityIterator newEntityIterator(Cursor cursor) {
+ return new EntityIteratorImpl(cursor);
}
private static class EntityIteratorImpl extends CursorEntityIterator {
- private final ContentResolver mResolver;
- private final ContentProviderClient mProvider;
- public EntityIteratorImpl(Cursor cursor, ContentResolver resolver) {
+ public EntityIteratorImpl(Cursor cursor) {
super(cursor);
- mResolver = resolver;
- mProvider = null;
- }
-
- public EntityIteratorImpl(Cursor cursor, ContentProviderClient provider) {
- super(cursor);
- mResolver = null;
- mProvider = provider;
}
@Override
@@ -274,7 +326,8 @@
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
Calendars.DISPLAY_NAME);
- DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.CALENDAR_COLOR);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+ Calendars.CALENDAR_COLOR);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS);
@@ -289,6 +342,8 @@
Calendars.CAN_MODIFY_TIME_ZONE);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
Calendars.MAX_REMINDERS);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+ Calendars.CAN_PARTIALLY_UPDATE);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
@@ -305,27 +360,40 @@
}
/**
- * Contains a list of available calendars.
+ * Fields and helpers for interacting with Calendars.
*/
- public static class Calendars implements BaseColumns, SyncColumns,
- CalendarsColumns
- {
+ public static class Calendars implements BaseColumns, SyncColumns, CalendarsColumns {
private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?"
+ " AND "
+ Calendars.ACCOUNT_TYPE + "=?";
- public static final Cursor query(ContentResolver cr, String[] projection,
- String where, String orderBy)
- {
- return cr.query(CONTENT_URI, projection, where,
- null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+ /**
+ * Helper function for generating a calendars query. This is blocking
+ * and should not be used on the UI thread. See
+ * {@link ContentResolver#query(Uri, String[], String, String[], String)}
+ * for more details about using the parameters.
+ *
+ * @param cr The ContentResolver to query with
+ * @param projection A list of columns to return
+ * @param selection A formatted selection string
+ * @param selectionArgs arguments to the selection string
+ * @param orderBy How to order the returned rows
+ * @return
+ */
+ public static final Cursor query(ContentResolver cr, String[] projection, String selection,
+ String[] selectionArgs, String orderBy) {
+ return cr.query(CONTENT_URI, projection, selection, selectionArgs,
+ orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
}
/**
- * Convenience method perform a delete on the Calendar provider
+ * Convenience method perform a delete on the Calendar provider. This is
+ * a blocking call and should not be used on the UI thread.
*
* @param cr the ContentResolver
- * @param selection the rows to delete
+ * @param selection A filter to apply to rows before deleting, formatted
+ * as an SQL WHERE clause (excluding the WHERE itself).
+ * @param selectionArgs Fill in the '?'s in the selection
* @return the count of rows that were deleted
*/
public static int delete(ContentResolver cr, String selection, String[] selectionArgs)
@@ -335,10 +403,12 @@
/**
* Convenience method to delete all calendars that match the account.
+ * This is a blocking call and should not be used on the UI thread.
*
* @param cr the ContentResolver
- * @param account the account whose rows should be deleted
- * @return the count of rows that were deleted
+ * @param account the account whose calendars and events should be
+ * deleted
+ * @return the count of calendar rows that were deleted
*/
public static int deleteCalendarsForAccount(ContentResolver cr, Account account) {
// delete all calendars that match this account
@@ -348,8 +418,9 @@
}
/**
- * The content:// style URL for this table
+ * The content:// style URL for accessing Calendars
*/
+ @SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars");
/**
@@ -358,43 +429,43 @@
public static final String DEFAULT_SORT_ORDER = "displayName";
/**
- * The URL to the calendar
+ * The URL to the calendar. Column name.
* <P>Type: TEXT (URL)</P>
*/
public static final String URL = "url";
/**
- * The URL for the calendar itself
+ * The URL for the calendar itself. Column name.
* <P>Type: TEXT (URL)</P>
*/
public static final String SELF_URL = "selfUrl";
/**
- * The URL for the calendar to be edited
+ * The URL for the calendar to be edited. Column name.
* <P>Type: TEXT (URL)</P>
*/
public static final String EDIT_URL = "editUrl";
/**
- * The URL for the calendar events
+ * The URL for the calendar events. Column name.
* <P>Type: TEXT (URL)</P>
*/
public static final String EVENTS_URL = "eventsUrl";
/**
- * The name of the calendar
+ * The name of the calendar. Column name.
* <P>Type: TEXT</P>
*/
public static final String NAME = "name";
/**
- * The display name of the calendar
+ * The display name of the calendar. Column name.
* <P>Type: TEXT</P>
*/
public static final String DISPLAY_NAME = "displayName";
/**
- * The location the of the events in the calendar
+ * The default location for the calendar. Column name.
* <P>Type: TEXT</P>
*/
public static final String CALENDAR_LOCATION = "calendar_location";
@@ -402,41 +473,45 @@
/**
* The owner account for this calendar, based on the calendar feed.
* This will be different from the _SYNC_ACCOUNT for delegated calendars.
+ * Column name.
* <P>Type: String</P>
*/
public static final String OWNER_ACCOUNT = "ownerAccount";
/**
* Can the organizer respond to the event? If no, the status of the
- * organizer should not be shown by the UI. Defaults to 1
+ * organizer should not be shown by the UI. Defaults to 1. Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String CAN_ORGANIZER_RESPOND = "canOrganizerRespond";
/**
- * Can the organizer modify the time zone of the event?
+ * Can the organizer modify the time zone of the event? Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String CAN_MODIFY_TIME_ZONE = "canModifyTimeZone";
/**
- * The maximum number of reminders allowed for an event.
+ * The maximum number of reminders allowed for an event. Column name.
* <P>Type: INTEGER</P>
*/
public static final String MAX_REMINDERS = "maxReminders";
/**
- * The maximum number of reminders allowed for an event.
- * <P>
- * Type: INTEGER
- * </P>
+ * A comma separated list of reminder methods supported for this
+ * calendar in the format "#,#,#". Valid types are
+ * {@link Reminders#METHOD_DEFAULT}, {@link Reminders#METHOD_ALERT},
+ * {@link Reminders#METHOD_EMAIL}, {@link Reminders#METHOD_SMS}. Column
+ * name.
+ * <P>Type: TEXT</P>
*/
public static final String ALLOWED_REMINDERS = "allowedReminders";
/**
* These fields are only writable by a sync adapter. To modify them the
- * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
- * _SYNC_ACCOUNT_TYPE in the query parameters.
+ * caller must include {@link #CALLER_IS_SYNCADAPTER},
+ * {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the query
+ * parameters.
*/
public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
ACCOUNT_NAME,
@@ -449,6 +524,7 @@
MAX_REMINDERS,
CAN_MODIFY_TIME_ZONE,
CAN_ORGANIZER_RESPOND,
+ CAN_PARTIALLY_UPDATE,
CALENDAR_LOCATION,
CALENDAR_TIMEZONE,
ACCESS_LEVEL,
@@ -457,7 +533,8 @@
CAL_SYNC2,
CAL_SYNC3,
CAL_SYNC4,
- CAL_SYNC5, CAL_SYNC6,
+ CAL_SYNC5,
+ CAL_SYNC6,
SYNC_STATE,
};
}
@@ -465,29 +542,29 @@
/**
* Columns from the Attendees table that other tables join into themselves.
*/
- public interface AttendeesColumns {
+ private interface AttendeesColumns {
/**
- * The id of the event.
+ * The id of the event. Column name.
* <P>Type: INTEGER</P>
*/
public static final String EVENT_ID = "event_id";
/**
- * The name of the attendee.
+ * The name of the attendee. Column name.
* <P>Type: STRING</P>
*/
public static final String ATTENDEE_NAME = "attendeeName";
/**
- * The email address of the attendee.
+ * The email address of the attendee. Column name.
* <P>Type: STRING</P>
*/
public static final String ATTENDEE_EMAIL = "attendeeEmail";
/**
- * The relationship of the attendee to the user.
- * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}.
+ * The relationship of the attendee to the user. Column name.
+ * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}.</P>
*/
public static final String ATTENDEE_RELATIONSHIP = "attendeeRelationship";
@@ -498,8 +575,8 @@
public static final int RELATIONSHIP_SPEAKER = 4;
/**
- * The type of attendee.
- * <P>Type: Integer (one of {@link #TYPE_REQUIRED}, {@link #TYPE_OPTIONAL})
+ * The type of attendee. Column name.
+ * <P>Type: Integer (one of {@link #TYPE_REQUIRED}, {@link #TYPE_OPTIONAL})</P>
*/
public static final String ATTENDEE_TYPE = "attendeeType";
@@ -508,8 +585,8 @@
public static final int TYPE_OPTIONAL = 2;
/**
- * The attendance status of the attendee.
- * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...}.
+ * The attendance status of the attendee. Column name.
+ * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...).</P>
*/
public static final String ATTENDEE_STATUS = "attendeeStatus";
@@ -520,59 +597,84 @@
public static final int ATTENDEE_STATUS_TENTATIVE = 4;
}
+ /**
+ * Fields and helpers for interacting with Attendees.
+ */
public static final class Attendees implements BaseColumns, AttendeesColumns, EventsColumns {
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees");
- // TODO: fill out this class when we actually start utilizing attendees
- // in the calendar application.
+ /**
+ * The content:// style URL for accessing Attendees data
+ */
+ @SuppressWarnings("hiding")
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees");
+ /**
+ * the projection used by the attendees query
+ */
+ public static final String[] PROJECTION = new String[] {
+ _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,};
+ private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
+
+ /**
+ * Queries all attendees associated with the given event. This is a
+ * blocking call and should not be done on the UI thread.
+ *
+ * @param cr The content resolver to use for the query
+ * @param eventId The id of the event to retrieve attendees for
+ * @return A Cursor containing all attendees for the event
+ */
+ public static final Cursor query(ContentResolver cr, long eventId) {
+ String[] attArgs = {Long.toString(eventId)};
+ return cr.query(CONTENT_URI, PROJECTION, ATTENDEES_WHERE, attArgs /* selection args */,
+ null /* sort order */);
+ }
}
/**
* Columns from the Events table that other tables join into themselves.
*/
- public interface EventsColumns {
+ private interface EventsColumns {
/**
- * For use by sync adapter at its discretion; not modified by CalendarProvider
- * Note that this column was formerly named _SYNC_LOCAL_ID. We are using it to avoid a
- * schema change.
- * TODO Replace this with something more general in the future.
+ * For use by sync adapter at its discretion. Column name.
+ * TODO change to sync_data2
* <P>Type: INTEGER (long)</P>
*/
public static final String _SYNC_DATA = "_sync_local_id";
/**
- * The calendar the event belongs to
- * <P>Type: INTEGER (foreign key to the Calendars table)</P>
+ * The {@link Calendars#_ID} of the calendar the event belongs to.
+ * Column name.
+ * <P>Type: INTEGER</P>
*/
public static final String CALENDAR_ID = "calendar_id";
/**
- * The URI for an HTML version of this event.
+ * The URI for an HTML version of this event. Column name.
+ * TODO change to sync_data3
* <P>Type: TEXT</P>
*/
public static final String HTML_URI = "htmlUri";
/**
- * The title of the event
+ * The title of the event. Column name.
* <P>Type: TEXT</P>
*/
public static final String TITLE = "title";
/**
- * The description of the event
+ * The description of the event. Column name.
* <P>Type: TEXT</P>
*/
public static final String DESCRIPTION = "description";
/**
- * Where the event takes place.
+ * Where the event takes place. Column name.
* <P>Type: TEXT</P>
*/
public static final String EVENT_LOCATION = "eventLocation";
/**
- * The event status
- * <P>Type: INTEGER (int)</P>
+ * The event status. Column name.
+ * <P>Type: INTEGER (one of {@link #STATUS_TENTATIVE}...)</P>
*/
public static final String STATUS = "eventStatus";
@@ -584,64 +686,82 @@
* This is a copy of the attendee status for the owner of this event.
* This field is copied here so that we can efficiently filter out
* events that are declined without having to look in the Attendees
- * table.
+ * table. Column name.
*
* <P>Type: INTEGER (int)</P>
*/
public static final String SELF_ATTENDEE_STATUS = "selfAttendeeStatus";
/**
- * This column is available for use by sync adapters
+ * This column is available for use by sync adapters. Column name.
* <P>Type: TEXT</P>
*/
public static final String SYNC_DATA1 = "sync_data1";
/**
- * The comments feed uri.
+ * This column is available for use by sync adapters
+ * <P>Type: TEXT</P>
+ */
+ public static final String SYNC_DATA7 = "sync_data7";
+
+ /**
+ * Used to indicate that a row is not a real event but an original copy of a locally
+ * modified event. A copy is made when an event changes from non-dirty to dirty and the
+ * event is on a calendar with {@link Calendars#CAN_PARTIALLY_UPDATE} set to 1. This copy
+ * does not get expanded in the instances table and is only visible in queries made by a
+ * sync adapter. The copy gets removed when the event is changed back to non-dirty by a
+ * sync adapter.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String LAST_SYNCED = "lastSynced";
+
+ /**
+ * The comments feed uri. Column name.
+ * TODO change to sync_data6
* <P>Type: TEXT</P>
*/
public static final String COMMENTS_URI = "commentsUri";
/**
- * The time the event starts
+ * The time the event starts in UTC millis since epoch. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String DTSTART = "dtstart";
/**
- * The time the event ends
+ * The time the event ends in UTC millis since epoch. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String DTEND = "dtend";
/**
- * The duration of the event
+ * The duration of the event in RFC2445 format. Column name.
* <P>Type: TEXT (duration in RFC2445 format)</P>
*/
public static final String DURATION = "duration";
/**
- * The timezone for the event.
- * <P>Type: TEXT
+ * The timezone for the event. Column name.
+ * <P>Type: TEXT</P>
*/
public static final String EVENT_TIMEZONE = "eventTimezone";
/**
- * The timezone for the event, allDay events will have a local tz instead of UTC
- * <P>Type: TEXT
+ * The timezone for the end time of the event. Column name.
+ * <P>Type: TEXT</P>
*/
public static final String EVENT_END_TIMEZONE = "eventEndTimezone";
/**
- * Whether the event lasts all day or not
+ * Is the event all day (time zone independent). Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String ALL_DAY = "allDay";
/**
* Defines how the event shows up for others when the calendar is
- * shared.
- * <P>Type: INTEGER</P>
+ * shared. Column name.
+ * <P>Type: INTEGER (One of {@link #ACCESS_DEFAULT}, ...)</P>
*/
public static final String ACCESS_LEVEL = "accessLevel";
@@ -655,20 +775,20 @@
*/
public static final int ACCESS_CONFIDENTIAL = 1;
/**
- * Private assumes the event appears as a free/busy slot with no
- * details.
+ * Private shares the event as a free/busy slot with no details.
*/
public static final int ACCESS_PRIVATE = 2;
/**
- * Public assumes the contents are visible to anyone with access to the
+ * Public makes the contents visible to anyone with access to the
* calendar.
*/
public static final int ACCESS_PUBLIC = 3;
/**
* If this event counts as busy time or is still free time that can be
- * scheduled over.
- * <P>Type: INTEGER</P>
+ * scheduled over. Column name.
+ * <P>Type: INTEGER (One of {@link #AVAILABILITY_BUSY},
+ * {@link #AVAILABILITY_FREE})</P>
*/
public static final String AVAILABILITY = "availability";
@@ -684,45 +804,44 @@
public static final int AVAILABILITY_FREE = 1;
/**
- * Whether the event has an alarm or not
+ * Whether the event has an alarm or not. Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String HAS_ALARM = "hasAlarm";
/**
- * Whether the event has extended properties or not
+ * Whether the event has extended properties or not. Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String HAS_EXTENDED_PROPERTIES = "hasExtendedProperties";
/**
- * The recurrence rule for the event.
- * than one.
+ * The recurrence rule for the event. Column name.
* <P>Type: TEXT</P>
*/
public static final String RRULE = "rrule";
/**
- * The recurrence dates for the event.
+ * The recurrence dates for the event. Column name.
* <P>Type: TEXT</P>
*/
public static final String RDATE = "rdate";
/**
- * The recurrence exception rule for the event.
+ * The recurrence exception rule for the event. Column name.
* <P>Type: TEXT</P>
*/
public static final String EXRULE = "exrule";
/**
- * The recurrence exception dates for the event.
+ * The recurrence exception dates for the event. Column name.
* <P>Type: TEXT</P>
*/
public static final String EXDATE = "exdate";
/**
- * The _id of the original recurring event for which this event is an
- * exception.
+ * The {@link Events#_ID} of the original recurring event for which this
+ * event is an exception. Column name.
* <P>Type: TEXT</P>
*/
public static final String ORIGINAL_ID = "original_id";
@@ -730,27 +849,28 @@
/**
* The _sync_id of the original recurring event for which this event is
* an exception. The provider should keep the original_id in sync when
- * this is updated.
+ * this is updated. Column name.
* <P>Type: TEXT</P>
*/
public static final String ORIGINAL_SYNC_ID = "original_sync_id";
/**
* The original instance time of the recurring event for which this
- * event is an exception.
+ * event is an exception. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String ORIGINAL_INSTANCE_TIME = "originalInstanceTime";
/**
* The allDay status (true or false) of the original recurring event
- * for which this event is an exception.
+ * for which this event is an exception. Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String ORIGINAL_ALL_DAY = "originalAllDay";
/**
- * The last date this event repeats on, or NULL if it never ends
+ * The last date this event repeats on, or NULL if it never ends. Column
+ * name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String LAST_DATE = "lastDate";
@@ -758,61 +878,66 @@
/**
* Whether the event has attendee information. True if the event
* has full attendee data, false if the event has information about
- * self only.
+ * self only. Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String HAS_ATTENDEE_DATA = "hasAttendeeData";
/**
- * Whether guests can modify the event.
+ * Whether guests can modify the event. Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String GUESTS_CAN_MODIFY = "guestsCanModify";
/**
- * Whether guests can invite other guests.
+ * Whether guests can invite other guests. Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String GUESTS_CAN_INVITE_OTHERS = "guestsCanInviteOthers";
/**
- * Whether guests can see the list of attendees.
+ * Whether guests can see the list of attendees. Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String GUESTS_CAN_SEE_GUESTS = "guestsCanSeeGuests";
/**
- * Email of the organizer (owner) of the event.
+ * Email of the organizer (owner) of the event. Column name.
* <P>Type: STRING</P>
*/
public static final String ORGANIZER = "organizer";
/**
- * Whether the user can invite others to the event.
- * The GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary guest,
- * while CAN_INVITE_OTHERS indicates if the user can invite others (either through
- * GUESTS_CAN_INVITE_OTHERS or because the user has modify access to the event).
+ * Whether the user can invite others to the event. The
+ * GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary
+ * guest, while CAN_INVITE_OTHERS indicates if the user can invite
+ * others (either through GUESTS_CAN_INVITE_OTHERS or because the user
+ * has modify access to the event). Column name.
* <P>Type: INTEGER (boolean, readonly)</P>
*/
public static final String CAN_INVITE_OTHERS = "canInviteOthers";
/**
* The owner account for this calendar, based on the calendar (foreign
- * key into the calendars table).
+ * key into the calendars table). Column name.
* <P>Type: String</P>
*/
public static final String OWNER_ACCOUNT = "ownerAccount";
/**
- * Whether the row has been deleted. A deleted row should be ignored.
+ * Whether the row has been deleted. A deleted row should be ignored.
+ * Column name.
* <P>Type: INTEGER (boolean)</P>
*/
public static final String DELETED = "deleted";
}
/**
- * Contains one entry per calendar event. Recurring events show up as a
- * single entry.
+ * Class that represents an Event Entity. There is one entry per event.
+ * Recurring events show up as a single entry. This is a helper class to
+ * make batch operations easier. A {@link ContentResolver} or
+ * {@link ContentProviderClient} is required as the helper does additional
+ * queries to add reminders and attendees to each entry.
*/
public static final class EventsEntity implements BaseColumns, SyncColumns, EventsColumns {
/**
@@ -821,10 +946,26 @@
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
"/event_entities");
+ /**
+ * Creates a new iterator for events
+ *
+ * @param cursor An event query
+ * @param resolver For performing additional queries
+ * @return an EntityIterator containing one entity per event in the
+ * cursor
+ */
public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
return new EntityIteratorImpl(cursor, resolver);
}
+ /**
+ * Creates a new iterator for events
+ *
+ * @param cursor An event query
+ * @param provider For performing additional queries
+ * @return an EntityIterator containing one entity per event in the
+ * cursor
+ */
public static EntityIterator newEntityIterator(Cursor cursor,
ContentProviderClient provider) {
return new EntityIteratorImpl(cursor, provider);
@@ -919,9 +1060,11 @@
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA);
+ DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, SYNC_DATA7);
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
+ DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_SYNCED);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
- DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC2);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC3);
@@ -1018,53 +1161,54 @@
}
/**
- * Contains one entry per calendar event. Recurring events show up as a single entry.
+ * Fields and helpers for interacting with Events.
*/
public static final class Events implements BaseColumns, SyncColumns, EventsColumns {
- private static final String[] FETCH_ENTRY_COLUMNS =
- new String[] { Events.ACCOUNT_NAME, Events._SYNC_ID };
-
- private static final String[] ATTENDEES_COLUMNS =
- new String[] { AttendeesColumns.ATTENDEE_NAME,
- AttendeesColumns.ATTENDEE_EMAIL,
- AttendeesColumns.ATTENDEE_RELATIONSHIP,
- AttendeesColumns.ATTENDEE_TYPE,
- AttendeesColumns.ATTENDEE_STATUS };
-
+ /**
+ * Queries all events with the given projection. This is a blocking call
+ * and should not be done on the UI thread.
+ *
+ * @param cr The content resolver to use for the query
+ * @param projection The columns to return
+ * @return A Cursor containing all events in the db
+ */
public static final Cursor query(ContentResolver cr, String[] projection) {
return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
}
- public static final Cursor query(ContentResolver cr, String[] projection,
- String where, String orderBy) {
- return cr.query(CONTENT_URI, projection, where,
- null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
- }
-
- private static String extractValue(ICalendar.Component component,
- String propertyName) {
- ICalendar.Property property =
- component.getFirstProperty(propertyName);
- if (property != null) {
- return property.getValue();
- }
- return null;
+ /**
+ * Queries events using the given projection, selection filter, and
+ * ordering. This is a blocking call and should not be done on the UI
+ * thread. For selection and selectionArgs usage see
+ * {@link ContentResolver#query(Uri, String[], String, String[], String)}
+ *
+ * @param cr The content resolver to use for the query
+ * @param projection The columns to return
+ * @param selection Filter on the query as an SQL WHERE statement
+ * @param selectionArgs Args to replace any '?'s in the selection
+ * @param orderBy How to order the rows as an SQL ORDER BY statement
+ * @return A Cursor containing the matching events
+ */
+ public static final Cursor query(ContentResolver cr, String[] projection, String selection,
+ String[] selectionArgs, String orderBy) {
+ return cr.query(CONTENT_URI, projection, selection, null,
+ orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
}
/**
- * The content:// style URL for this table
+ * The content:// style URL for interacting with events. Appending an
+ * event id using {@link ContentUris#withAppendedId(Uri, long)} will
+ * specify a single event.
*/
+ @SuppressWarnings("hiding")
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/events");
- public static final Uri DELETED_CONTENT_URI =
- Uri.parse("content://" + AUTHORITY + "/deleted_events");
-
/**
* The default sort order for this table
*/
- public static final String DEFAULT_SORT_ORDER = "";
+ private static final String DEFAULT_SORT_ORDER = "";
/**
* These are columns that should only ever be updated by the provider,
@@ -1073,7 +1217,14 @@
*/
public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
ACCOUNT_NAME,
- ACCOUNT_TYPE
+ ACCOUNT_TYPE,
+ CAL_SYNC1,
+ CAL_SYNC2,
+ CAL_SYNC3,
+ CAL_SYNC4,
+ CAL_SYNC5,
+ CAL_SYNC6,
+ CAN_PARTIALLY_UPDATE,
};
/**
@@ -1091,13 +1242,29 @@
}
/**
- * Contains one entry per calendar event instance. Recurring events show up every time
- * they occur.
+ * Fields and helpers for interacting with Instances. An instance is a
+ * single occurrence of an event including time zone specific start and end
+ * days and minutes.
*/
public static final class Instances implements BaseColumns, EventsColumns, CalendarsColumns {
private static final String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=1";
+ /**
+ * Performs a query to return all visible instances in the given range.
+ * This is a blocking function and should not be done on the UI thread.
+ * This will cause an expansion of recurring events to fill this time
+ * range if they are not already expanded and will slow down for larger
+ * time ranges with many recurring events.
+ *
+ * @param cr The ContentResolver to use for the query
+ * @param projection The columns to return
+ * @param begin The start of the time range to query in UTC millis since
+ * epoch
+ * @param end The end of the time range to query in UTC millis since
+ * epoch
+ * @return A Cursor containing all instances in the given range
+ */
public static final Cursor query(ContentResolver cr, String[] projection,
long begin, long end) {
Uri.Builder builder = CONTENT_URI.buildUpon();
@@ -1107,111 +1274,184 @@
null, DEFAULT_SORT_ORDER);
}
+ /**
+ * Performs a query to return all visible instances in the given range
+ * that match the given query. This is a blocking function and should
+ * not be done on the UI thread. This will cause an expansion of
+ * recurring events to fill this time range if they are not already
+ * expanded and will slow down for larger time ranges with many
+ * recurring events.
+ *
+ * @param cr The ContentResolver to use for the query
+ * @param projection The columns to return
+ * @param begin The start of the time range to query in UTC millis since
+ * epoch
+ * @param end The end of the time range to query in UTC millis since
+ * epoch
+ * @param searchQuery A string of space separated search terms. Segments
+ * enclosed by double quotes will be treated as a single
+ * term.
+ * @return A Cursor of instances matching the search terms in the given
+ * time range
+ */
public static final Cursor query(ContentResolver cr, String[] projection,
long begin, long end, String searchQuery) {
Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
ContentUris.appendId(builder, begin);
ContentUris.appendId(builder, end);
- return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED,
- new String[] { searchQuery }, DEFAULT_SORT_ORDER);
+ builder = builder.appendPath(searchQuery);
+ return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED, null,
+ DEFAULT_SORT_ORDER);
}
- public static final Cursor query(ContentResolver cr, String[] projection,
- long begin, long end, String where, String orderBy) {
+ /**
+ * Performs a query to return all visible instances in the given range
+ * that match the given selection. This is a blocking function and
+ * should not be done on the UI thread. This will cause an expansion of
+ * recurring events to fill this time range if they are not already
+ * expanded and will slow down for larger time ranges with many
+ * recurring events.
+ *
+ * @param cr The ContentResolver to use for the query
+ * @param projection The columns to return
+ * @param begin The start of the time range to query in UTC millis since
+ * epoch
+ * @param end The end of the time range to query in UTC millis since
+ * epoch
+ * @param selection Filter on the query as an SQL WHERE statement
+ * @param selectionArgs Args to replace any '?'s in the selection
+ * @param orderBy How to order the rows as an SQL ORDER BY statement
+ * @return A Cursor of instances matching the selection
+ */
+ public static final Cursor query(ContentResolver cr, String[] projection, long begin,
+ long end, String selection, String[] selectionArgs, String orderBy) {
Uri.Builder builder = CONTENT_URI.buildUpon();
ContentUris.appendId(builder, begin);
ContentUris.appendId(builder, end);
- if (TextUtils.isEmpty(where)) {
- where = WHERE_CALENDARS_SELECTED;
+ if (TextUtils.isEmpty(selection)) {
+ selection = WHERE_CALENDARS_SELECTED;
} else {
- where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED;
+ selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED;
}
- return cr.query(builder.build(), projection, where,
- null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
- }
-
- public static final Cursor query(ContentResolver cr, String[] projection, long begin,
- long end, String searchQuery, String where, String orderBy) {
- Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
- ContentUris.appendId(builder, begin);
- ContentUris.appendId(builder, end);
- builder = builder.appendPath(searchQuery);
- if (TextUtils.isEmpty(where)) {
- where = WHERE_CALENDARS_SELECTED;
- } else {
- where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED;
- }
- return cr.query(builder.build(), projection, where, null,
+ return cr.query(builder.build(), projection, selection, selectionArgs,
orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
}
/**
- * The content:// style URL for this table
+ * Performs a query to return all visible instances in the given range
+ * that match the given selection. This is a blocking function and
+ * should not be done on the UI thread. This will cause an expansion of
+ * recurring events to fill this time range if they are not already
+ * expanded and will slow down for larger time ranges with many
+ * recurring events.
+ *
+ * @param cr The ContentResolver to use for the query
+ * @param projection The columns to return
+ * @param begin The start of the time range to query in UTC millis since
+ * epoch
+ * @param end The end of the time range to query in UTC millis since
+ * epoch
+ * @param searchQuery A string of space separated search terms. Segments
+ * enclosed by double quotes will be treated as a single
+ * term.
+ * @param selection Filter on the query as an SQL WHERE statement
+ * @param selectionArgs Args to replace any '?'s in the selection
+ * @param orderBy How to order the rows as an SQL ORDER BY statement
+ * @return A Cursor of instances matching the selection
*/
+ public static final Cursor query(ContentResolver cr, String[] projection, long begin,
+ long end, String searchQuery, String selection, String[] selectionArgs,
+ String orderBy) {
+ Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
+ ContentUris.appendId(builder, begin);
+ ContentUris.appendId(builder, end);
+ builder = builder.appendPath(searchQuery);
+ if (TextUtils.isEmpty(selection)) {
+ selection = WHERE_CALENDARS_SELECTED;
+ } else {
+ selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED;
+ }
+ return cr.query(builder.build(), projection, selection, selectionArgs,
+ orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+ }
+
+ /**
+ * The content:// style URL for querying an instance range. The begin
+ * and end of the range to query should be added as path segments if
+ * this is used directly.
+ */
+ @SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
"/instances/when");
+ /**
+ * The content:// style URL for querying an instance range by Julian
+ * Day. The start and end day should be added as path segments if this
+ * is used directly.
+ */
public static final Uri CONTENT_BY_DAY_URI =
Uri.parse("content://" + AUTHORITY + "/instances/whenbyday");
+ /**
+ * The content:// style URL for querying an instance range with a search
+ * term. The begin, end, and search string should be appended as path
+ * segments if this is used directly.
+ */
public static final Uri CONTENT_SEARCH_URI = Uri.parse("content://" + AUTHORITY +
"/instances/search");
+ /**
+ * The content:// style URL for querying an instance range with a search
+ * term. The start day, end day, and search string should be appended as
+ * path segments if this is used directly.
+ */
public static final Uri CONTENT_SEARCH_BY_DAY_URI =
Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
/**
* The default sort order for this table.
*/
- public static final String DEFAULT_SORT_ORDER = "begin ASC";
+ private static final String DEFAULT_SORT_ORDER = "begin ASC";
/**
- * The sort order is: events with an earlier start time occur
- * first and if the start times are the same, then events with
- * a later end time occur first. The later end time is ordered
- * first so that long-running events in the calendar views appear
- * first. If the start and end times of two events are
- * the same then we sort alphabetically on the title. This isn't
- * required for correctness, it just adds a nice touch.
- */
- public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC";
- /**
- * The beginning time of the instance, in UTC milliseconds
+ * The beginning time of the instance, in UTC milliseconds. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String BEGIN = "begin";
/**
- * The ending time of the instance, in UTC milliseconds
+ * The ending time of the instance, in UTC milliseconds. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String END = "end";
/**
- * The event for this instance
+ * The _id of the event for this instance. Column name.
* <P>Type: INTEGER (long, foreign key to the Events table)</P>
*/
public static final String EVENT_ID = "event_id";
/**
- * The Julian start day of the instance, relative to the local timezone
+ * The Julian start day of the instance, relative to the local time
+ * zone. Column name.
* <P>Type: INTEGER (int)</P>
*/
public static final String START_DAY = "startDay";
/**
- * The Julian end day of the instance, relative to the local timezone
+ * The Julian end day of the instance, relative to the local time
+ * zone. Column name.
* <P>Type: INTEGER (int)</P>
*/
public static final String END_DAY = "endDay";
/**
* The start minute of the instance measured from midnight in the
- * local timezone.
+ * local time zone. Column name.
* <P>Type: INTEGER (int)</P>
*/
public static final String START_MINUTE = "startMinute";
/**
* The end minute of the instance measured from midnight in the
- * local timezone.
+ * local time zone. Column name.
* <P>Type: INTEGER (int)</P>
*/
public static final String END_MINUTE = "endMinute";
@@ -1219,14 +1459,12 @@
/**
* CalendarCache stores some settings for calendar including the current
- * time zone for the app. These settings are stored using a key/value
+ * time zone for the instaces. These settings are stored using a key/value
* scheme.
*/
- public interface CalendarCacheColumns {
+ private interface CalendarCacheColumns {
/**
- * The key for the setting. Keys are defined in CalendarChache in the
- * Calendar provider.
- * TODO Add keys to this file
+ * The key for the setting. Keys are defined in {@link CalendarCache}.
*/
public static final String KEY = "key";
@@ -1296,7 +1534,7 @@
* the Instances table and these are all stored in the first (and only)
* row of the CalendarMetaData table.
*/
- public interface CalendarMetaDataColumns {
+ private interface CalendarMetaDataColumns {
/**
* The local timezone that was used for precomputing the fields
* in the Instances table.
@@ -1330,34 +1568,51 @@
public static final String MAX_EVENTDAYS = "maxEventDays";
}
+ /**
+ * @hide
+ */
public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns {
}
- public interface EventDaysColumns {
+ private interface EventDaysColumns {
/**
- * The Julian starting day number.
+ * The Julian starting day number. Column name.
* <P>Type: INTEGER (int)</P>
*/
public static final String STARTDAY = "startDay";
+ /**
+ * The Julian ending day number. Column name.
+ * <P>Type: INTEGER (int)</P>
+ */
public static final String ENDDAY = "endDay";
}
+ /**
+ * Fields and helpers for querying for a list of days that contain events.
+ */
public static final class EventDays implements EventDaysColumns {
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
- "/instances/groupbyday");
-
- public static final String[] PROJECTION = { STARTDAY, ENDDAY };
- public static final String SELECTION = "selected=1";
+ private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+ + "/instances/groupbyday");
/**
- * Retrieves the days with events for the Julian days starting at "startDay"
- * for "numDays".
+ * The projection used by the EventDays query.
+ */
+ public static final String[] PROJECTION = { STARTDAY, ENDDAY };
+ private static final String SELECTION = "selected=1";
+
+ /**
+ * Retrieves the days with events for the Julian days starting at
+ * "startDay" for "numDays". It returns a cursor containing startday and
+ * endday representing the max range of days for all events beginning on
+ * each startday.This is a blocking function and should not be done on
+ * the UI thread.
*
* @param cr the ContentResolver
* @param startDay the first Julian day in the range
* @param numDays the number of days to load (must be at least 1)
- * @return a database cursor
+ * @return a database cursor containing a list of start and end days for
+ * events
*/
public static final Cursor query(ContentResolver cr, int startDay, int numDays) {
if (numDays < 1) {
@@ -1372,9 +1627,9 @@
}
}
- public interface RemindersColumns {
+ private interface RemindersColumns {
/**
- * The event the reminder belongs to
+ * The event the reminder belongs to. Column name.
* <P>Type: INTEGER (foreign key to the Events table)</P>
*/
public static final String EVENT_ID = "event_id";
@@ -1382,17 +1637,24 @@
/**
* The minutes prior to the event that the alarm should ring. -1
* specifies that we should use the default value for the system.
+ * Column name.
* <P>Type: INTEGER</P>
*/
public static final String MINUTES = "minutes";
+ /**
+ * Passing this as a minutes value will use the default reminder
+ * minutes.
+ */
public static final int MINUTES_DEFAULT = -1;
/**
- * The alarm method, as set on the server. DEFAULT, ALERT, EMAIL, and
- * SMS are possible values; the device will only process DEFAULT and
- * ALERT reminders (the other types are simply stored so we can send the
- * same reminder info back to the server when we make changes).
+ * The alarm method, as set on the server. {@link #METHOD_DEFAULT},
+ * {@link #METHOD_ALERT}, {@link #METHOD_EMAIL}, and {@link #METHOD_SMS}
+ * are possible values; the device will only process
+ * {@link #METHOD_DEFAULT} and {@link #METHOD_ALERT} reminders (the
+ * other types are simply stored so we can send the same reminder info
+ * back to the server when we make changes).
*/
public static final String METHOD = "method";
@@ -1402,61 +1664,85 @@
public static final int METHOD_SMS = 3;
}
+ /**
+ * Fields and helpers for accessing reminders for an event.
+ */
public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns {
- public static final String TABLE_NAME = "Reminders";
+ private static final String REMINDERS_WHERE = Calendar.Reminders.EVENT_ID + "=?";
+ /**
+ * The projection used by the reminders query.
+ */
+ public static final String[] PROJECTION = new String[] {
+ _ID, MINUTES, METHOD,};
+ @SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
+
+ /**
+ * Queries all reminders associated with the given event. This is a
+ * blocking call and should not be done on the UI thread.
+ *
+ * @param cr The content resolver to use for the query
+ * @param eventId The id of the event to retrieve reminders for
+ * @return A Cursor containing all reminders for the event
+ */
+ public static final Cursor query(ContentResolver cr, long eventId) {
+ String[] remArgs = {Long.toString(eventId)};
+ return cr.query(CONTENT_URI, PROJECTION, REMINDERS_WHERE, remArgs /* selection args */,
+ null /* sort order */);
+ }
}
- public interface CalendarAlertsColumns {
+ private interface CalendarAlertsColumns {
/**
- * The event that the alert belongs to
+ * The event that the alert belongs to. Column name.
* <P>Type: INTEGER (foreign key to the Events table)</P>
*/
public static final String EVENT_ID = "event_id";
/**
- * The start time of the event, in UTC
+ * The start time of the event, in UTC. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String BEGIN = "begin";
/**
- * The end time of the event, in UTC
+ * The end time of the event, in UTC. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String END = "end";
/**
- * The alarm time of the event, in UTC
+ * The alarm time of the event, in UTC. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String ALARM_TIME = "alarmTime";
/**
* The creation time of this database entry, in UTC.
- * (Useful for debugging missed reminders.)
+ * Useful for debugging missed reminders. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String CREATION_TIME = "creationTime";
/**
* The time that the alarm broadcast was received by the Calendar app,
- * in UTC. (Useful for debugging missed reminders.)
+ * in UTC. Useful for debugging missed reminders. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String RECEIVED_TIME = "receivedTime";
/**
* The time that the notification was created by the Calendar app,
- * in UTC. (Useful for debugging missed reminders.)
+ * in UTC. Useful for debugging missed reminders. Column name.
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String NOTIFY_TIME = "notifyTime";
/**
- * The state of this alert. It starts out as SCHEDULED, then when
- * the alarm goes off, it changes to FIRED, and then when the user
- * dismisses the alarm it changes to DISMISSED.
+ * The state of this alert. It starts out as {@link #SCHEDULED}, then
+ * when the alarm goes off, it changes to {@link #FIRED}, and then when
+ * the user dismisses the alarm it changes to {@link #DISMISSED}. Column
+ * name.
* <P>Type: INTEGER</P>
*/
public static final String STATE = "state";
@@ -1466,21 +1752,33 @@
public static final int DISMISSED = 2;
/**
- * The number of minutes that this alarm precedes the start time
- * <P>Type: INTEGER </P>
+ * The number of minutes that this alarm precedes the start time. Column
+ * name.
+ * <P>Type: INTEGER</P>
*/
public static final String MINUTES = "minutes";
/**
- * The default sort order for this table
+ * The default sort order for this alerts queries
*/
public static final String DEFAULT_SORT_ORDER = "begin ASC,title ASC";
}
+ /**
+ * Fields and helpers for accessing calendar alerts information. These
+ * fields are for tracking which alerts have been fired.
+ */
public static final class CalendarAlerts implements BaseColumns,
CalendarAlertsColumns, EventsColumns, CalendarsColumns {
+ /**
+ * @hide
+ */
public static final String TABLE_NAME = "CalendarAlerts";
+ /**
+ * The Uri for querying calendar alert information
+ */
+ @SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
"/calendar_alerts");
@@ -1507,6 +1805,11 @@
private static final boolean DEBUG = true;
+ /**
+ * Helper for inserting an alarm time associated with an event
+ *
+ * @hide
+ */
public static final Uri insert(ContentResolver cr, long eventId,
long begin, long end, long alarmTime, int minutes) {
ContentValues values = new ContentValues();
@@ -1523,6 +1826,19 @@
return cr.insert(CONTENT_URI, values);
}
+ /**
+ * Queries alerts info using the given projection, selection filter, and
+ * ordering. This is a blocking call and should not be done on the UI
+ * thread. For selection and selectionArgs usage see
+ * {@link ContentResolver#query(Uri, String[], String, String[], String)}
+ *
+ * @param cr The content resolver to use for the query
+ * @param projection The columns to return
+ * @param selection Filter on the query as an SQL WHERE statement
+ * @param selectionArgs Args to replace any '?'s in the selection
+ * @param sortOrder How to order the rows as an SQL ORDER BY statement
+ * @return A Cursor containing the matching alerts
+ */
public static final Cursor query(ContentResolver cr, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
return cr.query(CONTENT_URI, projection, selection, selectionArgs,
@@ -1531,12 +1847,13 @@
/**
* Finds the next alarm after (or equal to) the given time and returns
- * the time of that alarm or -1 if no such alarm exists.
+ * the time of that alarm or -1 if no such alarm exists. This is a
+ * blocking call and should not be done on the UI thread.
*
* @param cr the ContentResolver
* @param millis the time in UTC milliseconds
* @return the next alarm time greater than or equal to "millis", or -1
- * if no such alarm exists.
+ * if no such alarm exists.
*/
public static final long findNextAlarmTime(ContentResolver cr, long millis) {
String selection = ALARM_TIME + ">=" + millis;
@@ -1619,6 +1936,17 @@
}
}
+ /**
+ * Schedules an alarm intent with the system AlarmManager that will
+ * cause the Calendar provider to recheck alarms. This is used to wake
+ * the Calendar alarm handler when an alarm is expected or to do a
+ * periodic refresh of alarm data.
+ *
+ * @param context A context for referencing system resources
+ * @param manager The AlarmManager to use or null
+ * @param alarmTime The time to fire the intent in UTC millis since
+ * epoch
+ */
public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) {
if (DEBUG) {
Time time = new Time();
@@ -1678,27 +2006,32 @@
}
}
- public interface ExtendedPropertiesColumns {
+ private interface ExtendedPropertiesColumns {
/**
- * The event the extended property belongs to
+ * The event the extended property belongs to. Column name.
* <P>Type: INTEGER (foreign key to the Events table)</P>
*/
public static final String EVENT_ID = "event_id";
/**
* The name of the extended property. This is a uri of the form
- * {scheme}#{local-name} convention.
+ * {scheme}#{local-name} convention. Column name.
* <P>Type: TEXT</P>
*/
public static final String NAME = "name";
/**
- * The value of the extended property.
+ * The value of the extended property. Column name.
* <P>Type: TEXT</P>
*/
public static final String VALUE = "value";
}
+ /**
+ * Fields for accessing the Extended Properties. This is a generic set of
+ * name/value pairs for use by sync adapters or apps to add extra
+ * information to events.
+ */
public static final class ExtendedProperties implements BaseColumns,
ExtendedPropertiesColumns, EventsColumns {
public static final Uri CONTENT_URI =
@@ -1719,7 +2052,7 @@
*/
private SyncState() {}
- public static final String CONTENT_DIRECTORY =
+ private static final String CONTENT_DIRECTORY =
SyncStateContract.Constants.CONTENT_DIRECTORY;
/**
@@ -1732,39 +2065,43 @@
/**
* Columns from the EventsRawTimes table
*/
- public interface EventsRawTimesColumns {
+ private interface EventsRawTimesColumns {
/**
- * The corresponding event id
+ * The corresponding event id. Column name.
* <P>Type: INTEGER (long)</P>
*/
public static final String EVENT_ID = "event_id";
/**
- * The RFC2445 compliant time the event starts
+ * The RFC2445 compliant time the event starts. Column name.
* <P>Type: TEXT</P>
*/
public static final String DTSTART_2445 = "dtstart2445";
/**
- * The RFC2445 compliant time the event ends
+ * The RFC2445 compliant time the event ends. Column name.
* <P>Type: TEXT</P>
*/
public static final String DTEND_2445 = "dtend2445";
/**
- * The RFC2445 compliant original instance time of the recurring event for which this
- * event is an exception.
+ * The RFC2445 compliant original instance time of the recurring event
+ * for which this event is an exception. Column name.
* <P>Type: TEXT</P>
*/
public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445";
/**
- * The RFC2445 compliant last date this event repeats on, or NULL if it never ends
+ * The RFC2445 compliant last date this event repeats on, or NULL if it
+ * never ends. Column name.
* <P>Type: TEXT</P>
*/
public static final String LAST_DATE_2445 = "lastDate2445";
}
+ /**
+ * @hide
+ */
public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns {
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 893947d..6ab7738 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2924,6 +2924,30 @@
public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms";
/**
+ * Setting to turn off walled garden test on Wi-Fi. Feature is enabled by default and
+ * the setting needs to be set to 0 to disable it.
+ * @hide
+ */
+ public static final String WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED =
+ "wifi_watchdog_walled_garden_test_enabled";
+
+ /**
+ * The URL used for walled garden check upon a new conection. WifiWatchdogService
+ * fetches the URL and checks to see if {@link #WIFI_WATCHDOG_WALLED_GARDEN_PATTERN}
+ * is not part of the title string to notify the user on the presence of a walled garden.
+ * @hide
+ */
+ public static final String WIFI_WATCHDOG_WALLED_GARDEN_URL =
+ "wifi_watchdog_walled_garden_url";
+
+ /**
+ * The pattern string in the fetched URL used to detect a walled garden
+ * @hide
+ */
+ public static final String WIFI_WATCHDOG_WALLED_GARDEN_PATTERN =
+ "wifi_watchdog_walled_garden_pattern";
+
+ /**
* The maximum number of times we will retry a connection to an access
* point for which we have failed in acquiring an IP address from DHCP.
* A value of N means that we will make N+1 connection attempts in all.
@@ -3771,6 +3795,19 @@
public static final String DREAM_TIMEOUT =
"dream_timeout";
+ /** {@hide} */
+ public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
+ /** {@hide} */
+ public static final String NETSTATS_PERSIST_THRESHOLD = "netstats_persist_threshold";
+ /** {@hide} */
+ public static final String NETSTATS_SUMMARY_BUCKET_DURATION = "netstats_summary_bucket_duration";
+ /** {@hide} */
+ public static final String NETSTATS_SUMMARY_MAX_HISTORY = "netstats_summary_max_history";
+ /** {@hide} */
+ public static final String NETSTATS_DETAIL_BUCKET_DURATION = "netstats_detail_bucket_duration";
+ /** {@hide} */
+ public static final String NETSTATS_DETAIL_MAX_HISTORY = "netstats_detail_max_history";
+
/**
* @hide
*/
diff --git a/core/java/android/speech/tts/AudioMessageParams.java b/core/java/android/speech/tts/AudioMessageParams.java
new file mode 100644
index 0000000..db4d622
--- /dev/null
+++ b/core/java/android/speech/tts/AudioMessageParams.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011 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.speech.tts;
+
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+class AudioMessageParams extends MessageParams {
+ private final BlockingMediaPlayer mPlayer;
+
+ AudioMessageParams(UtteranceCompletedDispatcher dispatcher, BlockingMediaPlayer player) {
+ super(dispatcher);
+ mPlayer = player;
+ }
+
+ BlockingMediaPlayer getPlayer() {
+ return mPlayer;
+ }
+
+ @Override
+ int getType() {
+ return TYPE_AUDIO;
+ }
+
+}
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
new file mode 100644
index 0000000..924bbbc
--- /dev/null
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2011 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.speech.tts;
+
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.speech.tts.SynthesisMessageParams.ListEntry;
+import android.util.Log;
+
+class AudioPlaybackHandler extends Handler {
+ private static final String TAG = "TTS.AudioPlaybackHandler";
+ private static final boolean DBG = false;
+
+ private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
+
+ private static final int SYNTHESIS_START = 1;
+ private static final int SYNTHESIS_DATA_AVAILABLE = 2;
+ private static final int SYNTHESIS_COMPLETE_DATA_AVAILABLE = 3;
+ private static final int SYNTHESIS_DONE = 4;
+
+ private static final int PLAY_AUDIO = 5;
+ private static final int PLAY_SILENCE = 6;
+
+ // Accessed by multiple threads, synchronized by "this".
+ private MessageParams mCurrentParams;
+ // Used only for book keeping and error detection.
+ private SynthesisMessageParams mLastSynthesisRequest;
+
+ AudioPlaybackHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public synchronized void handleMessage(Message msg) {
+ if (msg.what == SYNTHESIS_START) {
+ mCurrentParams = (SynthesisMessageParams) msg.obj;
+ handleSynthesisStart(msg);
+ } else if (msg.what == SYNTHESIS_DATA_AVAILABLE) {
+ handleSynthesisDataAvailable(msg);
+ } else if (msg.what == SYNTHESIS_DONE) {
+ handleSynthesisDone(msg);
+ } else if (msg.what == SYNTHESIS_COMPLETE_DATA_AVAILABLE) {
+ handleSynthesisCompleteDataAvailable(msg);
+ } else if (msg.what == PLAY_AUDIO) {
+ handleAudio(msg);
+ } else if (msg.what == PLAY_SILENCE) {
+ handleSilence(msg);
+ }
+
+ mCurrentParams = null;
+ }
+
+ /**
+ * Stops all synthesis for a given {@code token}. If the current token
+ * is currently being processed, an effort will be made to stop it but
+ * that is not guaranteed.
+ */
+ synchronized public void stop(MessageParams token) {
+ removeCallbacksAndMessages(token);
+
+ if (token.getType() == MessageParams.TYPE_SYNTHESIS) {
+ sendMessageAtFrontOfQueue(obtainMessage(SYNTHESIS_DONE, token));
+ } else if (token == mCurrentParams) {
+ if (token.getType() == MessageParams.TYPE_AUDIO) {
+ ((AudioMessageParams) mCurrentParams).getPlayer().stop();
+ } else if (token.getType() == MessageParams.TYPE_SILENCE) {
+ ((SilenceMessageParams) mCurrentParams).getConditionVariable().open();
+ }
+ }
+ }
+
+ /**
+ * Shut down the audio playback thread.
+ */
+ synchronized public void quit() {
+ if (mCurrentParams != null) {
+ stop(mCurrentParams);
+ }
+ getLooper().quit();
+ }
+
+ void enqueueSynthesisStart(SynthesisMessageParams token) {
+ sendMessage(obtainMessage(SYNTHESIS_START, token));
+ }
+
+ void enqueueSynthesisDataAvailable(SynthesisMessageParams token) {
+ sendMessage(obtainMessage(SYNTHESIS_DATA_AVAILABLE, token));
+ }
+
+ void enqueueSynthesisCompleteDataAvailable(SynthesisMessageParams token) {
+ sendMessage(obtainMessage(SYNTHESIS_COMPLETE_DATA_AVAILABLE, token));
+ }
+
+ void enqueueSynthesisDone(SynthesisMessageParams token) {
+ sendMessage(obtainMessage(SYNTHESIS_DONE, token));
+ }
+
+ void enqueueAudio(AudioMessageParams token) {
+ sendMessage(obtainMessage(PLAY_AUDIO, token));
+ }
+
+ void enqueueSilence(SilenceMessageParams token) {
+ sendMessage(obtainMessage(PLAY_SILENCE, token));
+ }
+
+ // -----------------------------------------
+ // End of public API methods.
+ // -----------------------------------------
+
+ // Currently implemented as blocking the audio playback thread for the
+ // specified duration. If a call to stop() is made, the thread
+ // unblocks.
+ private void handleSilence(Message msg) {
+ if (DBG) Log.d(TAG, "handleSilence()");
+ SilenceMessageParams params = (SilenceMessageParams) msg.obj;
+ if (params.getSilenceDurationMs() > 0) {
+ params.getConditionVariable().block(params.getSilenceDurationMs());
+ }
+ params.getDispatcher().dispatchUtteranceCompleted();
+ if (DBG) Log.d(TAG, "handleSilence() done.");
+ }
+
+ // Plays back audio from a given URI. No TTS engine involvement here.
+ private void handleAudio(Message msg) {
+ if (DBG) Log.d(TAG, "handleAudio()");
+ AudioMessageParams params = (AudioMessageParams) msg.obj;
+ // Note that the BlockingMediaPlayer spawns a separate thread.
+ //
+ // TODO: This can be avoided.
+ params.getPlayer().startAndWait();
+ params.getDispatcher().dispatchUtteranceCompleted();
+ if (DBG) Log.d(TAG, "handleAudio() done.");
+ }
+
+ // Denotes the start of a new synthesis request. We create a new
+ // audio track, and prepare it for incoming data.
+ //
+ // Note that since all TTS synthesis happens on a single thread, we
+ // should ALWAYS see the following order :
+ //
+ // handleSynthesisStart -> handleSynthesisDataAvailable(*) -> handleSynthesisDone
+ // OR
+ // handleSynthesisCompleteDataAvailable.
+ private void handleSynthesisStart(Message msg) {
+ if (DBG) Log.d(TAG, "handleSynthesisStart()");
+ final SynthesisMessageParams param = (SynthesisMessageParams) msg.obj;
+
+ // Oops, looks like the engine forgot to call done(). We go through
+ // extra trouble to clean the data to prevent the AudioTrack resources
+ // from being leaked.
+ if (mLastSynthesisRequest != null) {
+ Log.w(TAG, "Error : Missing call to done() for request : " +
+ mLastSynthesisRequest);
+ handleSynthesisDone(mLastSynthesisRequest);
+ }
+
+ mLastSynthesisRequest = param;
+
+ // Create the audio track.
+ final AudioTrack audioTrack = createStreamingAudioTrack(
+ param.mStreamType, param.mSampleRateInHz, param.mAudioFormat,
+ param.mChannelCount, param.mVolume, param.mPan);
+
+ param.setAudioTrack(audioTrack);
+ }
+
+ // More data available to be flushed to the audio track.
+ private void handleSynthesisDataAvailable(Message msg) {
+ final SynthesisMessageParams param = (SynthesisMessageParams) msg.obj;
+ if (param.getAudioTrack() == null) {
+ Log.w(TAG, "Error : null audio track in handleDataAvailable.");
+ return;
+ }
+
+ if (param != mLastSynthesisRequest) {
+ Log.e(TAG, "Call to dataAvailable without done() / start()");
+ return;
+ }
+
+ final AudioTrack audioTrack = param.getAudioTrack();
+ final ListEntry bufferCopy = param.getNextBuffer();
+
+ if (bufferCopy == null) {
+ Log.e(TAG, "No buffers available to play.");
+ return;
+ }
+
+ int playState = audioTrack.getPlayState();
+ if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+ if (DBG) Log.d(TAG, "AudioTrack stopped, restarting : " + audioTrack.hashCode());
+ audioTrack.play();
+ }
+ int count = 0;
+ while (count < bufferCopy.mLength) {
+ // Note that we don't take bufferCopy.mOffset into account because
+ // it is guaranteed to be 0.
+ int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength);
+ if (written <= 0) {
+ break;
+ }
+ count += written;
+ }
+ }
+
+ private void handleSynthesisDone(Message msg) {
+ final SynthesisMessageParams params = (SynthesisMessageParams) msg.obj;
+ handleSynthesisDone(params);
+ }
+
+ // Flush all remaining data to the audio track, stop it and release
+ // all it's resources.
+ private void handleSynthesisDone(SynthesisMessageParams params) {
+ if (DBG) Log.d(TAG, "handleSynthesisDone()");
+ final AudioTrack audioTrack = params.getAudioTrack();
+
+ try {
+ if (audioTrack != null) {
+ audioTrack.flush();
+ audioTrack.stop();
+ audioTrack.release();
+ }
+ } finally {
+ params.setAudioTrack(null);
+ params.getDispatcher().dispatchUtteranceCompleted();
+ mLastSynthesisRequest = null;
+ }
+ }
+
+ private void handleSynthesisCompleteDataAvailable(Message msg) {
+ final SynthesisMessageParams params = (SynthesisMessageParams) msg.obj;
+ if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")");
+
+ // Channel config and bytes per frame are checked before
+ // this message is sent.
+ int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount);
+ int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(params.mAudioFormat);
+
+ ListEntry entry = params.getNextBuffer();
+
+ if (entry == null) {
+ Log.w(TAG, "completeDataAvailable : No buffers available to play.");
+ return;
+ }
+
+ final AudioTrack audioTrack = new AudioTrack(params.mStreamType, params.mSampleRateInHz,
+ channelConfig, params.mAudioFormat, entry.mLength, AudioTrack.MODE_STATIC);
+
+ // So that handleDone can access this correctly.
+ params.mAudioTrack = audioTrack;
+
+ try {
+ audioTrack.write(entry.mBytes, entry.mOffset, entry.mLength);
+ setupVolume(audioTrack, params.mVolume, params.mPan);
+ audioTrack.play();
+ blockUntilDone(audioTrack, bytesPerFrame, entry.mLength);
+ if (DBG) Log.d(TAG, "Wrote data to audio track successfully : " + entry.mLength);
+ } catch (IllegalStateException ex) {
+ Log.e(TAG, "Playback error", ex);
+ } finally {
+ handleSynthesisDone(msg);
+ }
+ }
+
+
+ private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
+ int lengthInFrames = length / bytesPerFrame;
+ int currentPosition = 0;
+ while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
+ long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
+ audioTrack.getSampleRate();
+ audioTrack.getPlayState();
+ if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
+ " Playback position : " + currentPosition);
+ try {
+ Thread.sleep(estimatedTimeMs);
+ } catch (InterruptedException ie) {
+ break;
+ }
+ }
+ }
+
+ private static AudioTrack createStreamingAudioTrack(int streamType, int sampleRateInHz,
+ int audioFormat, int channelCount, float volume, float pan) {
+ int channelConfig = getChannelConfig(channelCount);
+
+ int minBufferSizeInBytes
+ = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
+ int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
+
+ AudioTrack audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig,
+ audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
+ if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+ Log.w(TAG, "Unable to create audio track.");
+ audioTrack.release();
+ return null;
+ }
+
+ setupVolume(audioTrack, volume, pan);
+ return audioTrack;
+ }
+
+ static int getChannelConfig(int channelCount) {
+ if (channelCount == 1) {
+ return AudioFormat.CHANNEL_OUT_MONO;
+ } else if (channelCount == 2){
+ return AudioFormat.CHANNEL_OUT_STEREO;
+ }
+
+ return 0;
+ }
+
+ static int getBytesPerFrame(int audioFormat) {
+ if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
+ return 1;
+ } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
+ return 2;
+ }
+
+ return -1;
+ }
+
+ private static void setupVolume(AudioTrack audioTrack, float volume, float pan) {
+ float vol = clip(volume, 0.0f, 1.0f);
+ float panning = clip(pan, -1.0f, 1.0f);
+ float volLeft = vol;
+ float volRight = vol;
+ if (panning > 0.0f) {
+ volLeft *= (1.0f - panning);
+ } else if (panning < 0.0f) {
+ volRight *= (1.0f + panning);
+ }
+ if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
+ if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
+ Log.e(TAG, "Failed to set volume");
+ }
+ }
+
+ private static float clip(float value, float min, float max) {
+ return value > max ? max : (value < min ? min : value);
+ }
+
+}
diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java
new file mode 100644
index 0000000..2d96df4
--- /dev/null
+++ b/core/java/android/speech/tts/MessageParams.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 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.speech.tts;
+
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+abstract class MessageParams {
+ private final UtteranceCompletedDispatcher mDispatcher;
+
+ static final int TYPE_SYNTHESIS = 1;
+ static final int TYPE_AUDIO = 2;
+ static final int TYPE_SILENCE = 3;
+
+ MessageParams(UtteranceCompletedDispatcher dispatcher) {
+ mDispatcher = dispatcher;
+ }
+
+ UtteranceCompletedDispatcher getDispatcher() {
+ return mDispatcher;
+ }
+
+ abstract int getType();
+}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
index d698b54..34b263c 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -15,10 +15,8 @@
*/
package android.speech.tts;
-import android.media.AudioFormat;
-import android.media.AudioTrack;
import android.os.Bundle;
-import android.os.Handler;
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
import android.util.Log;
/**
@@ -49,54 +47,48 @@
*/
private final float mPan;
+ /**
+ * Guards {@link #mAudioTrackHandler}, {@link #mToken} and {@link #mStopped}.
+ */
private final Object mStateLock = new Object();
- private final Handler mAudioTrackHandler;
- private volatile AudioTrack mAudioTrack = null;
- private boolean mStopped = false;
- private boolean mDone = false;
- private volatile boolean mWriteErrorOccured;
- PlaybackSynthesisRequest(String text, Bundle params,
- int streamType, float volume, float pan, Handler audioTrackHandler) {
+ // Handler associated with a thread that plays back audio requests.
+ private final AudioPlaybackHandler mAudioTrackHandler;
+ // A request "token", which will be non null after start() or
+ // completeAudioAvailable() have been called.
+ private SynthesisMessageParams mToken = null;
+ // Whether this request has been stopped. This is useful for keeping
+ // track whether stop() has been called before start(). In all other cases,
+ // a non-null value of mToken will provide the same information.
+ private boolean mStopped = false;
+
+ private volatile boolean mDone = false;
+
+ private final UtteranceCompletedDispatcher mDispatcher;
+
+ PlaybackSynthesisRequest(String text, Bundle params, int streamType, float volume, float pan,
+ AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher) {
super(text, params);
mStreamType = streamType;
mVolume = volume;
mPan = pan;
mAudioTrackHandler = audioTrackHandler;
- mWriteErrorOccured = false;
+ mDispatcher = dispatcher;
}
@Override
void stop() {
if (DBG) Log.d(TAG, "stop()");
+
synchronized (mStateLock) {
- mStopped = true;
- cleanUp();
- }
- }
-
- // Always guarded by mStateLock.
- private void cleanUp() {
- if (DBG) Log.d(TAG, "cleanUp()");
- if (mAudioTrack == null) {
- return;
- }
-
- final AudioTrack audioTrack = mAudioTrack;
- mAudioTrack = null;
-
- // Clean up on the audiotrack handler thread.
- //
- // NOTE: It isn't very clear whether AudioTrack is thread safe.
- // If it is we can clean up on the current (synthesis) thread.
- mAudioTrackHandler.post(new Runnable() {
- @Override
- public void run() {
- audioTrack.flush();
- audioTrack.stop();
- audioTrack.release();
+ if (mToken == null || mStopped) {
+ Log.w(TAG, "stop() called twice, before start(), or after done()");
+ return;
}
- });
+ mAudioTrackHandler.stop(mToken);
+ mToken = null;
+ mStopped = true;
+ }
}
@Override
@@ -111,7 +103,6 @@
return mDone;
}
- // TODO: add a thread that writes to the AudioTrack?
@Override
public int start(int sampleRateInHz, int audioFormat, int channelCount) {
if (DBG) {
@@ -119,45 +110,28 @@
+ "," + channelCount + ")");
}
+ int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
+ if (channelConfig == 0) {
+ Log.e(TAG, "Unsupported number of channels :" + channelCount);
+ return TextToSpeech.ERROR;
+ }
+
synchronized (mStateLock) {
if (mStopped) {
- if (DBG) Log.d(TAG, "Request has been aborted.");
+ if (DBG) Log.d(TAG, "stop() called before start(), returning.");
return TextToSpeech.ERROR;
}
- if (mAudioTrack != null) {
- Log.e(TAG, "start() called twice");
- cleanUp();
- return TextToSpeech.ERROR;
- }
+ SynthesisMessageParams params = new SynthesisMessageParams(
+ mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
+ mDispatcher);
+ mAudioTrackHandler.enqueueSynthesisStart(params);
- mAudioTrack = createStreamingAudioTrack(sampleRateInHz, audioFormat, channelCount);
- if (mAudioTrack == null) {
- return TextToSpeech.ERROR;
- }
+ mToken = params;
}
return TextToSpeech.SUCCESS;
}
- private void setupVolume(AudioTrack audioTrack, float volume, float pan) {
- float vol = clip(volume, 0.0f, 1.0f);
- float panning = clip(pan, -1.0f, 1.0f);
- float volLeft = vol;
- float volRight = vol;
- if (panning > 0.0f) {
- volLeft *= (1.0f - panning);
- } else if (panning < 0.0f) {
- volRight *= (1.0f + panning);
- }
- if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
- if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
- Log.e(TAG, "Failed to set volume");
- }
- }
-
- private float clip(float value, float min, float max) {
- return value > max ? max : (value < min ? min : value);
- }
@Override
public int audioAvailable(byte[] buffer, int offset, int length) {
@@ -169,195 +143,78 @@
throw new IllegalArgumentException("buffer is too large or of zero length (" +
+ length + " bytes)");
}
+
synchronized (mStateLock) {
- if (mWriteErrorOccured) {
- if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+ if (mToken == null) {
return TextToSpeech.ERROR;
}
- if (mStopped) {
- if (DBG) Log.d(TAG, "Request has been aborted.");
- return TextToSpeech.ERROR;
- }
- if (mAudioTrack == null) {
- Log.e(TAG, "audioAvailable(): Not started");
- return TextToSpeech.ERROR;
- }
- final AudioTrack audioTrack = mAudioTrack;
+
// Sigh, another copy.
final byte[] bufferCopy = new byte[length];
System.arraycopy(buffer, offset, bufferCopy, 0, length);
-
- mAudioTrackHandler.post(new Runnable() {
- @Override
- public void run() {
- int playState = audioTrack.getPlayState();
- if (playState == AudioTrack.PLAYSTATE_STOPPED) {
- if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
- audioTrack.play();
- }
- // TODO: loop until all data is written?
- if (DBG) Log.d(TAG, "AudioTrack.write()");
- int count = audioTrack.write(bufferCopy, 0, bufferCopy.length);
- // The semantics of this change very slightly. Earlier, we would
- // report an error immediately, Now we will return an error on
- // the next API call, usually done( ) or another audioAvailable( )
- // call.
- if (count < 0) {
- mWriteErrorOccured = true;
- }
- }
- });
-
- return TextToSpeech.SUCCESS;
+ mToken.addBuffer(bufferCopy);
+ mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken);
}
+
+ return TextToSpeech.SUCCESS;
}
@Override
public int done() {
if (DBG) Log.d(TAG, "done()");
+
synchronized (mStateLock) {
- if (mWriteErrorOccured) {
- if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+ if (mDone) {
+ Log.w(TAG, "Duplicate call to done()");
return TextToSpeech.ERROR;
}
- if (mStopped) {
- if (DBG) Log.d(TAG, "Request has been aborted.");
- return TextToSpeech.ERROR;
- }
- if (mAudioTrack == null) {
- Log.e(TAG, "done(): Not started");
- return TextToSpeech.ERROR;
- }
+
mDone = true;
- cleanUp();
+
+ if (mToken == null) {
+ return TextToSpeech.ERROR;
+ }
+
+ mAudioTrackHandler.enqueueSynthesisDone(mToken);
}
return TextToSpeech.SUCCESS;
}
@Override
public void error() {
- if (DBG) Log.d(TAG, "error()");
- synchronized (mStateLock) {
- cleanUp();
- }
+ if (DBG) Log.d(TAG, "error() [will call stop]");
+ stop();
}
@Override
public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
byte[] buffer, int offset, int length) {
- if (DBG) {
- Log.d(TAG, "completeAudioAvailable(" + sampleRateInHz + "," + audioFormat
- + "," + channelCount + "byte[" + buffer.length + "],"
- + offset + "," + length + ")");
+ int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
+ if (channelConfig == 0) {
+ Log.e(TAG, "Unsupported number of channels :" + channelCount);
+ return TextToSpeech.ERROR;
+ }
+
+ int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(audioFormat);
+ if (bytesPerFrame < 0) {
+ Log.e(TAG, "Unsupported audio format :" + audioFormat);
+ return TextToSpeech.ERROR;
}
synchronized (mStateLock) {
if (mStopped) {
- if (DBG) Log.d(TAG, "Request has been aborted.");
return TextToSpeech.ERROR;
}
- if (mAudioTrack != null) {
- Log.e(TAG, "start() called before completeAudioAvailable()");
- cleanUp();
- return TextToSpeech.ERROR;
- }
+ SynthesisMessageParams params = new SynthesisMessageParams(
+ mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
+ mDispatcher);
+ params.addBuffer(buffer, offset, length);
- int channelConfig = getChannelConfig(channelCount);
- if (channelConfig < 0) {
- Log.e(TAG, "Unsupported number of channels :" + channelCount);
- cleanUp();
- return TextToSpeech.ERROR;
- }
- int bytesPerFrame = getBytesPerFrame(audioFormat);
- if (bytesPerFrame < 0) {
- Log.e(TAG, "Unsupported audio format :" + audioFormat);
- cleanUp();
- return TextToSpeech.ERROR;
- }
-
- mAudioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
- audioFormat, buffer.length, AudioTrack.MODE_STATIC);
- if (mAudioTrack == null) {
- return TextToSpeech.ERROR;
- }
-
- try {
- mAudioTrack.write(buffer, offset, length);
- setupVolume(mAudioTrack, mVolume, mPan);
- mAudioTrack.play();
- blockUntilDone(mAudioTrack, bytesPerFrame, length);
- mDone = true;
- if (DBG) Log.d(TAG, "Wrote data to audio track succesfully : " + length);
- } catch (IllegalStateException ex) {
- Log.e(TAG, "Playback error", ex);
- return TextToSpeech.ERROR;
- } finally {
- cleanUp();
- }
+ mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params);
+ mToken = params;
}
return TextToSpeech.SUCCESS;
}
- private void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
- int lengthInFrames = length / bytesPerFrame;
- int currentPosition = 0;
- while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
- long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
- audioTrack.getSampleRate();
- if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
- " Playback position : " + currentPosition);
- try {
- Thread.sleep(estimatedTimeMs);
- } catch (InterruptedException ie) {
- break;
- }
- }
- }
-
- private int getBytesPerFrame(int audioFormat) {
- if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
- return 1;
- } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
- return 2;
- }
-
- return -1;
- }
-
- private int getChannelConfig(int channelCount) {
- if (channelCount == 1) {
- return AudioFormat.CHANNEL_OUT_MONO;
- } else if (channelCount == 2){
- return AudioFormat.CHANNEL_OUT_STEREO;
- }
-
- return -1;
- }
-
- private AudioTrack createStreamingAudioTrack(int sampleRateInHz, int audioFormat,
- int channelCount) {
- int channelConfig = getChannelConfig(channelCount);
-
- if (channelConfig < 0) {
- Log.e(TAG, "Unsupported number of channels : " + channelCount);
- return null;
- }
-
- int minBufferSizeInBytes
- = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
- int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
- AudioTrack audioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
- audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
- if (audioTrack == null) {
- return null;
- }
-
- if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
- audioTrack.release();
- return null;
- }
- setupVolume(audioTrack, mVolume, mPan);
- return audioTrack;
- }
}
diff --git a/core/java/android/speech/tts/SilenceMessageParams.java b/core/java/android/speech/tts/SilenceMessageParams.java
new file mode 100644
index 0000000..eee8b68
--- /dev/null
+++ b/core/java/android/speech/tts/SilenceMessageParams.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 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.speech.tts;
+
+import android.os.ConditionVariable;
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+class SilenceMessageParams extends MessageParams {
+ private final ConditionVariable mCondVar = new ConditionVariable();
+ private final long mSilenceDurationMs;
+
+ SilenceMessageParams(UtteranceCompletedDispatcher dispatcher, long silenceDurationMs) {
+ super(dispatcher);
+ mSilenceDurationMs = silenceDurationMs;
+ }
+
+ long getSilenceDurationMs() {
+ return mSilenceDurationMs;
+ }
+
+ @Override
+ int getType() {
+ return TYPE_SILENCE;
+ }
+
+ ConditionVariable getConditionVariable() {
+ return mCondVar;
+ }
+
+}
diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java
new file mode 100644
index 0000000..aabaa5a
--- /dev/null
+++ b/core/java/android/speech/tts/SynthesisMessageParams.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 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.speech.tts;
+
+import android.media.AudioTrack;
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+import java.util.LinkedList;
+
+/**
+ * Params required to play back a synthesis request.
+ */
+final class SynthesisMessageParams extends MessageParams {
+ final int mStreamType;
+ final int mSampleRateInHz;
+ final int mAudioFormat;
+ final int mChannelCount;
+ final float mVolume;
+ final float mPan;
+
+ public volatile AudioTrack mAudioTrack;
+
+ private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>();
+
+ SynthesisMessageParams(int streamType, int sampleRate,
+ int audioFormat, int channelCount,
+ float volume, float pan, UtteranceCompletedDispatcher dispatcher) {
+ super(dispatcher);
+
+ mStreamType = streamType;
+ mSampleRateInHz = sampleRate;
+ mAudioFormat = audioFormat;
+ mChannelCount = channelCount;
+ mVolume = volume;
+ mPan = pan;
+
+ // initially null.
+ mAudioTrack = null;
+ }
+
+ @Override
+ int getType() {
+ return TYPE_SYNTHESIS;
+ }
+
+ synchronized void addBuffer(byte[] buffer, int offset, int length) {
+ mDataBufferList.add(new ListEntry(buffer, offset, length));
+ }
+
+ synchronized void addBuffer(byte[] buffer) {
+ mDataBufferList.add(new ListEntry(buffer, 0, buffer.length));
+ }
+
+ synchronized ListEntry getNextBuffer() {
+ return mDataBufferList.poll();
+ }
+
+
+ void setAudioTrack(AudioTrack audioTrack) {
+ mAudioTrack = audioTrack;
+ }
+
+ AudioTrack getAudioTrack() {
+ return mAudioTrack;
+ }
+
+ static final class ListEntry {
+ final byte[] mBytes;
+ final int mOffset;
+ final int mLength;
+
+ ListEntry(byte[] bytes, int offset, int length) {
+ mBytes = bytes;
+ mOffset = offset;
+ mLength = length;
+ }
+ }
+}
+
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index ddd3252..e553f77 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -51,7 +51,10 @@
private static final String SYNTH_THREAD_NAME = "SynthThread";
private SynthHandler mSynthHandler;
- private Handler mAudioTrackHandler;
+ // A thread and it's associated handler for playing back any audio
+ // associated with this TTS engine. Will handle all requests except synthesis
+ // to file requests, which occur on the synthesis thread.
+ private AudioPlaybackHandler mAudioPlaybackHandler;
private CallbackMap mCallbacks;
@@ -68,7 +71,7 @@
HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread");
audioTrackThread.start();
- mAudioTrackHandler = new Handler(audioTrackThread.getLooper());
+ mAudioPlaybackHandler = new AudioPlaybackHandler(audioTrackThread.getLooper());
mCallbacks = new CallbackMap();
@@ -83,8 +86,8 @@
// Tell the synthesizer to stop
mSynthHandler.quit();
- mAudioTrackHandler.getLooper().quit();
-
+ // Tell the audio playback thread to stop.
+ mAudioPlaybackHandler.quit();
// Unregister all callbacks.
mCallbacks.kill();
@@ -236,13 +239,6 @@
super(looper);
}
- private void dispatchUtteranceCompleted(SpeechItem item) {
- String utteranceId = item.getUtteranceId();
- if (!TextUtils.isEmpty(utteranceId)) {
- mCallbacks.dispatchUtteranceCompleted(item.getCallingApp(), utteranceId);
- }
- }
-
private synchronized SpeechItem getCurrentSpeechItem() {
return mCurrentSpeechItem;
}
@@ -286,9 +282,7 @@
@Override
public void run() {
setCurrentSpeechItem(speechItem);
- if (speechItem.play() == TextToSpeech.SUCCESS) {
- dispatchUtteranceCompleted(speechItem);
- }
+ speechItem.play();
setCurrentSpeechItem(null);
}
};
@@ -318,14 +312,19 @@
if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
current.stop();
}
+
return TextToSpeech.SUCCESS;
}
}
+ interface UtteranceCompletedDispatcher {
+ public void dispatchUtteranceCompleted();
+ }
+
/**
* An item in the synth thread queue.
*/
- private static abstract class SpeechItem {
+ private abstract class SpeechItem implements UtteranceCompletedDispatcher {
private final String mCallingApp;
protected final Bundle mParams;
private boolean mStarted = false;
@@ -380,6 +379,13 @@
stopImpl();
}
+ public void dispatchUtteranceCompleted() {
+ final String utteranceId = getUtteranceId();
+ if (!TextUtils.isEmpty(utteranceId)) {
+ mCallbacks.dispatchUtteranceCompleted(getCallingApp(), utteranceId);
+ }
+ }
+
protected abstract int playImpl();
protected abstract void stopImpl();
@@ -413,7 +419,7 @@
}
}
- private class SynthesisSpeechItem extends SpeechItem {
+ class SynthesisSpeechItem extends SpeechItem {
private final String mText;
private SynthesisRequest mSynthesisRequest;
@@ -453,7 +459,8 @@
protected SynthesisRequest createSynthesisRequest() {
return new PlaybackSynthesisRequest(mText, mParams,
- getStreamType(), getVolume(), getPan(), mAudioTrackHandler);
+ getStreamType(), getVolume(), getPan(), mAudioPlaybackHandler,
+ this);
}
private void setRequestParams(SynthesisRequest request) {
@@ -526,6 +533,15 @@
return new FileSynthesisRequest(getText(), mParams, mFile);
}
+ @Override
+ protected int playImpl() {
+ int status = super.playImpl();
+ if (status == TextToSpeech.SUCCESS) {
+ dispatchUtteranceCompleted();
+ }
+ return status;
+ }
+
/**
* Checks that the given file can be used for synthesis output.
*/
@@ -557,6 +573,7 @@
private class AudioSpeechItem extends SpeechItem {
private final BlockingMediaPlayer mPlayer;
+ private AudioMessageParams mToken;
public AudioSpeechItem(String callingApp, Bundle params, Uri uri) {
super(callingApp, params);
@@ -570,23 +587,26 @@
@Override
protected int playImpl() {
- return mPlayer.startAndWait() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
+ mToken = new AudioMessageParams(this, mPlayer);
+ mAudioPlaybackHandler.enqueueAudio(mToken);
+ return TextToSpeech.SUCCESS;
}
@Override
protected void stopImpl() {
- mPlayer.stop();
+ if (mToken != null) {
+ mAudioPlaybackHandler.stop(mToken);
+ }
}
}
private class SilenceSpeechItem extends SpeechItem {
private final long mDuration;
- private final ConditionVariable mDone;
+ private SilenceMessageParams mToken;
public SilenceSpeechItem(String callingApp, Bundle params, long duration) {
super(callingApp, params);
mDuration = duration;
- mDone = new ConditionVariable();
}
@Override
@@ -596,13 +616,16 @@
@Override
protected int playImpl() {
- boolean aborted = mDone.block(mDuration);
- return aborted ? TextToSpeech.ERROR : TextToSpeech.SUCCESS;
+ mToken = new SilenceMessageParams(this, mDuration);
+ mAudioPlaybackHandler.enqueueSilence(mToken);
+ return TextToSpeech.SUCCESS;
}
@Override
protected void stopImpl() {
- mDone.open();
+ if (mToken != null) {
+ mAudioPlaybackHandler.stop(mToken);
+ }
}
}
diff --git a/core/java/android/util/FinitePool.java b/core/java/android/util/FinitePool.java
index 3ef8293..4ae21ad 100644
--- a/core/java/android/util/FinitePool.java
+++ b/core/java/android/util/FinitePool.java
@@ -69,6 +69,7 @@
if (element != null) {
element.setNextPoolable(null);
+ element.setPooled(false);
mManager.onAcquired(element);
}
@@ -76,9 +77,13 @@
}
public void release(T element) {
+ if (element.isPooled()) {
+ throw new IllegalArgumentException("Element already in the pool.");
+ }
if (mInfinite || mPoolCount < mLimit) {
mPoolCount++;
element.setNextPoolable(mRoot);
+ element.setPooled(true);
mRoot = element;
}
mManager.onReleased(element);
diff --git a/core/java/android/util/Poolable.java b/core/java/android/util/Poolable.java
index fd9bd9b..87e0529 100644
--- a/core/java/android/util/Poolable.java
+++ b/core/java/android/util/Poolable.java
@@ -22,4 +22,6 @@
public interface Poolable<T> {
void setNextPoolable(T element);
T getNextPoolable();
+ boolean isPooled();
+ void setPooled(boolean isPooled);
}
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index b2a35d3..69e6489 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -81,8 +81,10 @@
* horizontal axis. */
public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT;
- /** Raw bit controlling whether the horizontal direction is relative (before/after) or not. */
- public static final int RELATIVE_HORIZONTAL_DIRECTION = 0x00800000;
+ /** Raw bit controlling whether the layout direction is relative or not (START/END instead of
+ * absolute LEFT/RIGHT).
+ */
+ public static final int RELATIVE_LAYOUT_DIRECTION = 0x00800000;
/**
* Binary mask to get the absolute horizontal gravity of a gravity.
@@ -109,16 +111,16 @@
*/
public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;
- /** Push object to x-axis position before its container, not changing its size. */
- public static final int BEFORE = RELATIVE_HORIZONTAL_DIRECTION | LEFT;
+ /** Push object to x-axis position at the start of its container, not changing its size. */
+ public static final int START = RELATIVE_LAYOUT_DIRECTION | LEFT;
- /** Push object to x-axis position after its container, not changing its size. */
- public static final int AFTER = RELATIVE_HORIZONTAL_DIRECTION | RIGHT;
+ /** Push object to x-axis position at the end of its container, not changing its size. */
+ public static final int END = RELATIVE_LAYOUT_DIRECTION | RIGHT;
/**
* Binary mask for the horizontal gravity and script specific direction bit.
*/
- public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = BEFORE | AFTER;
+ public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;
/**
* Apply a gravity constant to an object. This suppose that the layout direction is LTR.
@@ -342,8 +344,8 @@
/**
* <p>Convert script specific gravity to absolute horizontal value.</p>
*
- * if horizontal direction is LTR, then BEFORE will set LEFT and AFTER will set RIGHT.
- * if horizontal direction is RTL, then BEFORE will set RIGHT and AFTER will set LEFT.
+ * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
+ * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
*
* @param gravity The gravity to convert to absolute (horizontal) values.
* @param isRtl Whether the layout is right-to-left.
@@ -351,11 +353,11 @@
*/
public static int getAbsoluteGravity(int gravity, boolean isRtl) {
int result = gravity;
- // If layout is script specific and gravity is horizontal relative (BEFORE or AFTER)
- if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) {
- if ((result & Gravity.BEFORE) == Gravity.BEFORE) {
- // Remove the BEFORE bit
- result &= ~BEFORE;
+ // If layout is script specific and gravity is horizontal relative (START or END)
+ if ((result & RELATIVE_LAYOUT_DIRECTION) > 0) {
+ if ((result & Gravity.START) == Gravity.START) {
+ // Remove the START bit
+ result &= ~START;
if (isRtl) {
// Set the RIGHT bit
result |= RIGHT;
@@ -363,9 +365,9 @@
// Set the LEFT bit
result |= LEFT;
}
- } else if ((result & Gravity.AFTER) == Gravity.AFTER) {
- // Remove the AFTER bit
- result &= ~AFTER;
+ } else if ((result & Gravity.END) == Gravity.END) {
+ // Remove the END bit
+ result &= ~END;
if (isRtl) {
// Set the LEFT bit
result |= LEFT;
@@ -376,7 +378,7 @@
}
// Don't need the script specific bit any more, so remove it as we are converting to
// absolute values (LEFT or RIGHT)
- result &= ~RELATIVE_HORIZONTAL_DIRECTION;
+ result &= ~RELATIVE_LAYOUT_DIRECTION;
}
return result;
}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index fccef2b..5a91d31 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -50,6 +50,7 @@
private int mPtr;
private VelocityTracker mNext;
+ private boolean mIsPooled;
private static native int nativeInitialize();
private static native void nativeDispose(int ptr);
@@ -93,6 +94,20 @@
return mNext;
}
+ /**
+ * @hide
+ */
+ public boolean isPooled() {
+ return mIsPooled;
+ }
+
+ /**
+ * @hide
+ */
+ public void setPooled(boolean isPooled) {
+ mIsPooled = isPooled;
+ }
+
private VelocityTracker() {
mPtr = nativeInitialize();
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 014f19f..70218ac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -61,6 +61,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
@@ -950,51 +951,51 @@
/**
* Horizontal direction of this view is from Left to Right.
- * Use with {@link #setHorizontalDirection}.
+ * Use with {@link #setLayoutDirection}.
* {@hide}
*/
- public static final int HORIZONTAL_DIRECTION_LTR = 0x00000000;
+ public static final int LAYOUT_DIRECTION_LTR = 0x00000000;
/**
* Horizontal direction of this view is from Right to Left.
- * Use with {@link #setHorizontalDirection}.
+ * Use with {@link #setLayoutDirection}.
* {@hide}
*/
- public static final int HORIZONTAL_DIRECTION_RTL = 0x40000000;
+ public static final int LAYOUT_DIRECTION_RTL = 0x40000000;
/**
* Horizontal direction of this view is inherited from its parent.
- * Use with {@link #setHorizontalDirection}.
+ * Use with {@link #setLayoutDirection}.
* {@hide}
*/
- public static final int HORIZONTAL_DIRECTION_INHERIT = 0x80000000;
+ public static final int LAYOUT_DIRECTION_INHERIT = 0x80000000;
/**
* Horizontal direction of this view is from deduced from the default language
- * script for the locale. Use with {@link #setHorizontalDirection}.
+ * script for the locale. Use with {@link #setLayoutDirection}.
* {@hide}
*/
- public static final int HORIZONTAL_DIRECTION_LOCALE = 0xC0000000;
+ public static final int LAYOUT_DIRECTION_LOCALE = 0xC0000000;
/**
* Mask for use with setFlags indicating bits used for horizontalDirection.
* {@hide}
*/
- static final int HORIZONTAL_DIRECTION_MASK = 0xC0000000;
+ static final int LAYOUT_DIRECTION_MASK = 0xC0000000;
/*
* Array of horizontal direction flags for mapping attribute "horizontalDirection" to correct
* flag value.
* {@hide}
*/
- private static final int[] HORIZONTAL_DIRECTION_FLAGS = { HORIZONTAL_DIRECTION_LTR,
- HORIZONTAL_DIRECTION_RTL, HORIZONTAL_DIRECTION_INHERIT, HORIZONTAL_DIRECTION_LOCALE};
+ private static final int[] LAYOUT_DIRECTION_FLAGS = {LAYOUT_DIRECTION_LTR,
+ LAYOUT_DIRECTION_RTL, LAYOUT_DIRECTION_INHERIT, LAYOUT_DIRECTION_LOCALE};
/**
* Default horizontalDirection.
* {@hide}
*/
- private static final int HORIZONTAL_DIRECTION_DEFAULT = HORIZONTAL_DIRECTION_INHERIT;
+ private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
/**
* View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
@@ -1492,6 +1493,11 @@
private static final Object sTagsLock = new Object();
/**
+ * The next available accessiiblity id.
+ */
+ private static int sNextAccessibilityViewId;
+
+ /**
* The animation currently associated with this view.
* @hide
*/
@@ -1533,6 +1539,11 @@
int mID = NO_ID;
/**
+ * The stable ID of this view for accessibility porposes.
+ */
+ int mAccessibilityViewId = NO_ID;
+
+ /**
* The view's tag.
* {@hide}
*
@@ -2453,7 +2464,7 @@
public View(Context context) {
mContext = context;
mResources = context != null ? context.getResources() : null;
- mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | HORIZONTAL_DIRECTION_INHERIT;
+ mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | LAYOUT_DIRECTION_INHERIT;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
}
@@ -2651,18 +2662,18 @@
viewFlagMasks |= VISIBILITY_MASK;
}
break;
- case com.android.internal.R.styleable.View_horizontalDirection:
+ case com.android.internal.R.styleable.View_layoutDirection:
// Clear any HORIZONTAL_DIRECTION flag already set
- viewFlagValues &= ~HORIZONTAL_DIRECTION_MASK;
+ viewFlagValues &= ~LAYOUT_DIRECTION_MASK;
// Set the HORIZONTAL_DIRECTION flags depending on the value of the attribute
- final int horizontalDirection = a.getInt(attr, -1);
- if (horizontalDirection != -1) {
- viewFlagValues |= HORIZONTAL_DIRECTION_FLAGS[horizontalDirection];
+ final int layoutDirection = a.getInt(attr, -1);
+ if (layoutDirection != -1) {
+ viewFlagValues |= LAYOUT_DIRECTION_FLAGS[layoutDirection];
} else {
- // Set to default (HORIZONTAL_DIRECTION_INHERIT)
- viewFlagValues |= HORIZONTAL_DIRECTION_DEFAULT;
+ // Set to default (LAYOUT_DIRECTION_INHERIT)
+ viewFlagValues |= LAYOUT_DIRECTION_DEFAULT;
}
- viewFlagMasks |= HORIZONTAL_DIRECTION_MASK;
+ viewFlagMasks |= LAYOUT_DIRECTION_MASK;
break;
case com.android.internal.R.styleable.View_drawingCacheQuality:
final int cacheQuality = a.getInt(attr, 0);
@@ -3649,6 +3660,7 @@
* @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent)
*/
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ event.setSource(this);
event.setClassName(getClass().getName());
event.setPackageName(getContext().getPackageName());
event.setEnabled(isEnabled());
@@ -3664,6 +3676,112 @@
}
/**
+ * Returns an {@link AccessibilityNodeInfo} representing this view from the
+ * point of view of an {@link android.accessibilityservice.AccessibilityService}.
+ * This method is responsible for obtaining an accessibility node info from a
+ * pool of reusable instances and calling
+ * {@link #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} on this view to
+ * initialize the former.
+ * <p>
+ * Note: The client is responsible for recycling the obtained instance by calling
+ * {@link AccessibilityNodeInfo#recycle()} to minimize object creation.
+ * </p>
+ * @return A populated {@link AccessibilityNodeInfo}.
+ *
+ * @see AccessibilityNodeInfo
+ */
+ public AccessibilityNodeInfo createAccessibilityNodeInfo() {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(this);
+ onInitializeAccessibilityNodeInfo(info);
+ return info;
+ }
+
+ /**
+ * Initializes an {@link AccessibilityNodeInfo} with information about this view.
+ * The base implementation sets:
+ * <ul>
+ * <li>{@link AccessibilityNodeInfo#setParent(View)},</li>
+ * <li>{@link AccessibilityNodeInfo#setBounds(Rect)},</li>
+ * <li>{@link AccessibilityNodeInfo#setPackageName(CharSequence)},</li>
+ * <li>{@link AccessibilityNodeInfo#setClassName(CharSequence)},</li>
+ * <li>{@link AccessibilityNodeInfo#setContentDescription(CharSequence)},</li>
+ * <li>{@link AccessibilityNodeInfo#setEnabled(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfo#setClickable(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfo#setFocusable(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfo#setFocused(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfo#setLongClickable(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfo#setSelected(boolean)},</li>
+ * </ul>
+ * <p>
+ * Subclasses should override this method, call the super implementation,
+ * and set additional attributes.
+ * </p>
+ * @param info The instance to initialize.
+ */
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ Rect bounds = mAttachInfo.mTmpInvalRect;
+ getDrawingRect(bounds);
+ info.setBounds(bounds);
+
+ ViewParent parent = getParent();
+ if (parent instanceof View) {
+ View parentView = (View) parent;
+ info.setParent(parentView);
+ }
+
+ info.setPackageName(mContext.getPackageName());
+ info.setClassName(getClass().getName());
+ info.setContentDescription(getContentDescription());
+
+ info.setEnabled(isEnabled());
+ info.setClickable(isClickable());
+ info.setFocusable(isFocusable());
+ info.setFocused(isFocused());
+ info.setSelected(isSelected());
+ info.setLongClickable(isLongClickable());
+
+ // TODO: These make sense only if we are in an AdapterView but all
+ // views can be selected. Maybe from accessiiblity perspective
+ // we should report as selectable view in an AdapterView.
+ info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
+ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
+
+ if (isFocusable()) {
+ if (isFocused()) {
+ info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
+ } else {
+ info.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
+ }
+ }
+ }
+
+ /**
+ * Gets the unique identifier of this view on the screen for accessibility purposes.
+ * If this {@link View} is not attached to any window, {@value #NO_ID} is returned.
+ *
+ * @return The view accessibility id.
+ *
+ * @hide
+ */
+ public int getAccessibilityViewId() {
+ if (mAccessibilityViewId == NO_ID) {
+ mAccessibilityViewId = sNextAccessibilityViewId++;
+ }
+ return mAccessibilityViewId;
+ }
+
+ /**
+ * Gets the unique identifier of the window in which this View reseides.
+ *
+ * @return The window accessibility id.
+ *
+ * @hide
+ */
+ public int getAccessibilityWindowId() {
+ return mAttachInfo != null ? mAttachInfo.mAccessibilityWindowId : NO_ID;
+ }
+
+ /**
* Gets the {@link View} description. It briefly describes the view and is
* primarily used for accessibility support. Set this property to enable
* better accessibility support for your application. This is especially
@@ -4138,38 +4256,38 @@
}
/**
- * Returns the horizontal direction for this view.
+ * Returns the layout direction for this view.
*
- * @return One of {@link #HORIZONTAL_DIRECTION_LTR},
- * {@link #HORIZONTAL_DIRECTION_RTL},
- * {@link #HORIZONTAL_DIRECTION_INHERIT} or
- * {@link #HORIZONTAL_DIRECTION_LOCALE}.
- * @attr ref android.R.styleable#View_horizontalDirection
+ * @return One of {@link #LAYOUT_DIRECTION_LTR},
+ * {@link #LAYOUT_DIRECTION_RTL},
+ * {@link #LAYOUT_DIRECTION_INHERIT} or
+ * {@link #LAYOUT_DIRECTION_LOCALE}.
+ * @attr ref android.R.styleable#View_layoutDirection
* @hide
*/
@ViewDebug.ExportedProperty(category = "layout", mapping = {
- @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LTR, to = "LTR"),
- @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_RTL, to = "RTL"),
- @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_INHERIT, to = "INHERIT"),
- @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LOCALE, to = "LOCALE")
+ @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "LTR"),
+ @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RTL"),
+ @ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE, to = "LOCALE")
})
- public int getHorizontalDirection() {
- return mViewFlags & HORIZONTAL_DIRECTION_MASK;
+ public int getLayoutDirection() {
+ return mViewFlags & LAYOUT_DIRECTION_MASK;
}
/**
- * Set the horizontal direction for this view.
+ * Set the layout direction for this view.
*
- * @param horizontalDirection One of {@link #HORIZONTAL_DIRECTION_LTR},
- * {@link #HORIZONTAL_DIRECTION_RTL},
- * {@link #HORIZONTAL_DIRECTION_INHERIT} or
- * {@link #HORIZONTAL_DIRECTION_LOCALE}.
- * @attr ref android.R.styleable#View_horizontalDirection
+ * @param layoutDirection One of {@link #LAYOUT_DIRECTION_LTR},
+ * {@link #LAYOUT_DIRECTION_RTL},
+ * {@link #LAYOUT_DIRECTION_INHERIT} or
+ * {@link #LAYOUT_DIRECTION_LOCALE}.
+ * @attr ref android.R.styleable#View_layoutDirection
* @hide
*/
@RemotableViewMethod
- public void setHorizontalDirection(int horizontalDirection) {
- setFlags(horizontalDirection, HORIZONTAL_DIRECTION_MASK);
+ public void setLayoutDirection(int layoutDirection) {
+ setFlags(layoutDirection, LAYOUT_DIRECTION_MASK);
}
/**
@@ -4571,6 +4689,16 @@
}
/**
+ * Finds the Views that contain given text. The containment is case insensitive.
+ * As View's text is considered any text content that View renders.
+ *
+ * @param outViews The output list of matching Views.
+ * @param text The text to match against.
+ */
+ public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+ }
+
+ /**
* Find and return all touchable views that are descendants of this view,
* possibly including this view if it is touchable itself.
*
@@ -4677,8 +4805,8 @@
// need to be focusable in touch mode if in touch mode
if (isInTouchMode() &&
- (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
- return false;
+ (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
+ return false;
}
// need to not have any parents blocking us
@@ -5975,7 +6103,7 @@
}
}
- if ((changed & HORIZONTAL_DIRECTION_MASK) != 0) {
+ if ((changed & LAYOUT_DIRECTION_MASK) != 0) {
requestLayout();
}
}
@@ -8530,24 +8658,24 @@
mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH;
}
jumpDrawablesToCurrentState();
- resolveHorizontalDirection();
+ resolveLayoutDirection();
}
/**
* Resolving the layout direction. LTR is set initially.
* We are supposing here that the parent directionality will be resolved before its children
*/
- private void resolveHorizontalDirection() {
+ private void resolveLayoutDirection() {
mPrivateFlags2 &= ~RESOLVED_LAYOUT_RTL;
- switch (getHorizontalDirection()) {
- case HORIZONTAL_DIRECTION_INHERIT:
+ switch (getLayoutDirection()) {
+ case LAYOUT_DIRECTION_INHERIT:
// If this is root view, no need to look at parent's layout dir.
if (mParent != null && mParent instanceof ViewGroup &&
((ViewGroup) mParent).isLayoutRtl()) {
mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
}
break;
- case HORIZONTAL_DIRECTION_RTL:
+ case LAYOUT_DIRECTION_RTL:
mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
break;
}
@@ -12719,6 +12847,7 @@
);
private InvalidateInfo mNext;
+ private boolean mIsPooled;
View target;
@@ -12742,6 +12871,14 @@
void release() {
sPool.release(this);
}
+
+ public boolean isPooled() {
+ return mIsPooled;
+ }
+
+ public void setPooled(boolean isPooled) {
+ mIsPooled = isPooled;
+ }
}
final IWindowSession mSession;
@@ -12952,6 +13089,11 @@
final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
/**
+ * The id of the window for accessibility purposes.
+ */
+ int mAccessibilityWindowId = View.NO_ID;
+
+ /**
* Creates a new set of attachment information with the specified
* events handler and thread.
*
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index 1e72529..9ab4c82 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -50,18 +50,28 @@
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
+import android.util.Pool;
+import android.util.Poolable;
+import android.util.PoolableManager;
+import android.util.Pools;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
+
import com.android.internal.policy.PolicyManager;
+import com.android.internal.util.Predicate;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;
@@ -71,6 +81,7 @@
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.List;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -248,6 +259,12 @@
*/
AudioManager mAudioManager;
+ final AccessibilityManager mAccessibilityManager;
+
+ AccessibilityInteractionController mAccessibilityInteractionContrtoller;
+
+ AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager;
+
private final int mDensity;
/**
@@ -285,7 +302,7 @@
// done here instead of in the static block because Zygote does not
// allow the spawning of threads.
getWindowSession(context.getMainLooper());
-
+
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
@@ -302,6 +319,11 @@
mPreviousTransparentRegion = new Region();
mFirst = true; // true for the first time the view is added
mAdded = false;
+ mAccessibilityManager = AccessibilityManager.getInstance(context);
+ mAccessibilityInteractionConnectionManager =
+ new AccessibilityInteractionConnectionManager();
+ mAccessibilityManager.addAccessibilityStateChangeListener(
+ mAccessibilityInteractionConnectionManager);
mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
mViewConfiguration = ViewConfiguration.get(context);
mDensity = context.getResources().getDisplayMetrics().densityDpi;
@@ -490,10 +512,14 @@
InputQueue.registerInputChannel(mInputChannel, mInputHandler,
Looper.myQueue());
}
-
+
view.assignParent(this);
mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
+
+ if (mAccessibilityManager.isEnabled()) {
+ mAccessibilityInteractionConnectionManager.ensureConnection();
+ }
}
}
}
@@ -2004,6 +2030,10 @@
mView.dispatchDetachedFromWindow();
}
+ mAccessibilityInteractionConnectionManager.ensureNoConnection();
+ mAccessibilityManager.removeAccessibilityStateChangeListener(
+ mAccessibilityInteractionConnectionManager);
+
mView = null;
mAttachInfo.mRootView = null;
mAttachInfo.mSurface = null;
@@ -2020,7 +2050,6 @@
InputQueue.unregisterInputChannel(mInputChannel);
}
}
-
try {
sWindowSession.remove(mWindow);
} catch (RemoteException e) {
@@ -2098,6 +2127,10 @@
public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016;
public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017;
public final static int DISPATCH_GENERIC_MOTION = 1018;
+ public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1019;
+ public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1020;
+ public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1021;
+ public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1022;
@Override
public void handleMessage(Message msg) {
@@ -2298,9 +2331,25 @@
case DISPATCH_SYSTEM_UI_VISIBILITY: {
handleDispatchSystemUiVisibilityChanged(msg.arg1);
} break;
+ case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
+ getAccessibilityInteractionController()
+ .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg);
+ } break;
+ case DO_PERFORM_ACCESSIBILITY_ACTION: {
+ getAccessibilityInteractionController()
+ .perfromAccessibilityActionUiThread(msg);
+ } break;
+ case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
+ getAccessibilityInteractionController()
+ .findAccessibilityNodeInfoByViewIdUiThread(msg);
+ } break;
+ case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: {
+ getAccessibilityInteractionController()
+ .findAccessibilityNodeInfosByViewTextUiThread(msg);
+ } break;
}
}
-
+
private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
if (mFinishedCallback != null) {
Slog.w(TAG, "Received a new input event from the input queue but there is "
@@ -3220,6 +3269,17 @@
return mAudioManager;
}
+ public AccessibilityInteractionController getAccessibilityInteractionController() {
+ if (mView == null) {
+ throw new IllegalStateException("getAccessibilityInteractionController"
+ + " called when there is no mView");
+ }
+ if (mAccessibilityInteractionContrtoller == null) {
+ mAccessibilityInteractionContrtoller = new AccessibilityInteractionController();
+ }
+ return mAccessibilityInteractionContrtoller;
+ }
+
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
@@ -3517,7 +3577,7 @@
* send an {@link AccessibilityEvent} to announce that.
*/
private void sendAccessibilityEvents() {
- if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) {
+ if (!mAccessibilityManager.isEnabled()) {
return;
}
mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -3545,7 +3605,7 @@
if (mView == null) {
return false;
}
- AccessibilityManager.getInstance(child.mContext).sendAccessibilityEvent(event);
+ mAccessibilityManager.sendAccessibilityEvent(event);
return true;
}
@@ -3608,18 +3668,18 @@
static class InputMethodCallback extends IInputMethodCallback.Stub {
private WeakReference<ViewAncestor> mViewAncestor;
- public InputMethodCallback(ViewAncestor viewRoot) {
- mViewAncestor = new WeakReference<ViewAncestor>(viewRoot);
+ public InputMethodCallback(ViewAncestor viewAncestor) {
+ mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor);
}
public void finishedEvent(int seq, boolean handled) {
- final ViewAncestor viewRoot = mViewAncestor.get();
- if (viewRoot != null) {
- viewRoot.dispatchFinishedEvent(seq, handled);
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchFinishedEvent(seq, handled);
}
}
- public void sessionCreated(IInputMethodSession session) throws RemoteException {
+ public void sessionCreated(IInputMethodSession session) {
// Stub -- not for use in the client.
}
}
@@ -3627,36 +3687,37 @@
static class W extends IWindow.Stub {
private final WeakReference<ViewAncestor> mViewAncestor;
- W(ViewAncestor viewRoot) {
- mViewAncestor = new WeakReference<ViewAncestor>(viewRoot);
+ W(ViewAncestor viewAncestor) {
+ mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor);
}
public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets,
boolean reportDraw, Configuration newConfig) {
- final ViewAncestor viewRoot = mViewAncestor.get();
- if (viewRoot != null) {
- viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig);
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw,
+ newConfig);
}
}
public void dispatchAppVisibility(boolean visible) {
- final ViewAncestor viewRoot = mViewAncestor.get();
- if (viewRoot != null) {
- viewRoot.dispatchAppVisibility(visible);
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchAppVisibility(visible);
}
}
public void dispatchGetNewSurface() {
- final ViewAncestor viewRoot = mViewAncestor.get();
- if (viewRoot != null) {
- viewRoot.dispatchGetNewSurface();
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchGetNewSurface();
}
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
- final ViewAncestor viewRoot = mViewAncestor.get();
- if (viewRoot != null) {
- viewRoot.windowFocusChanged(hasFocus, inTouchMode);
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
}
}
@@ -3674,9 +3735,9 @@
}
public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
- final ViewAncestor viewRoot = mViewAncestor.get();
- if (viewRoot != null) {
- final View view = viewRoot.mView;
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ final View view = viewAncestor.mView;
if (view != null) {
if (checkCallingPermission(Manifest.permission.DUMP) !=
PackageManager.PERMISSION_GRANTED) {
@@ -3705,9 +3766,9 @@
}
public void closeSystemDialogs(String reason) {
- final ViewAncestor viewRoot = mViewAncestor.get();
- if (viewRoot != null) {
- viewRoot.dispatchCloseSystemDialogs(reason);
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchCloseSystemDialogs(reason);
}
}
@@ -3720,7 +3781,7 @@
}
}
}
-
+
public void dispatchWallpaperCommand(String action, int x, int y,
int z, Bundle extras, boolean sync) {
if (sync) {
@@ -3733,17 +3794,16 @@
/* Drag/drop */
public void dispatchDragEvent(DragEvent event) {
- final ViewAncestor viewRoot = mViewAncestor.get();
- if (viewRoot != null) {
- viewRoot.dispatchDragEvent(event);
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchDragEvent(event);
}
}
- @Override
public void dispatchSystemUiVisibilityChanged(int visibility) {
- final ViewAncestor viewRoot = mViewAncestor.get();
- if (viewRoot != null) {
- viewRoot.dispatchSystemUiVisibilityChanged(visibility);
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchSystemUiVisibilityChanged(visibility);
}
}
}
@@ -4053,5 +4113,395 @@
}
}
+ /**
+ * Class for managing the accessibility interaction connection
+ * based on the global accessibility state.
+ */
+ final class AccessibilityInteractionConnectionManager
+ implements AccessibilityStateChangeListener {
+ public void onAccessibilityStateChanged(boolean enabled) {
+ if (enabled) {
+ ensureConnection();
+ } else {
+ ensureNoConnection();
+ }
+ }
+
+ public void ensureConnection() {
+ final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
+ if (!registered) {
+ mAttachInfo.mAccessibilityWindowId =
+ mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
+ new AccessibilityInteractionConnection(ViewAncestor.this));
+ }
+ }
+
+ public void ensureNoConnection() {
+ final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
+ if (registered) {
+ mAttachInfo.mAccessibilityWindowId = View.NO_ID;
+ mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
+ }
+ }
+ }
+
+ /**
+ * This class is an interface this ViewAncestor provides to the
+ * AccessibilityManagerService to the latter can interact with
+ * the view hierarchy in this ViewAncestor.
+ */
+ final class AccessibilityInteractionConnection
+ extends IAccessibilityInteractionConnection.Stub {
+ private final WeakReference<ViewAncestor> mViewAncestor;
+
+ AccessibilityInteractionConnection(ViewAncestor viewAncestor) {
+ mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor);
+ }
+
+ public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor == null) {
+ return;
+ }
+ getAccessibilityInteractionController()
+ .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId,
+ interactionId, callback);
+ }
+
+ public void performAccessibilityAction(int accessibilityId, int action,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor == null) {
+ return;
+ }
+ getAccessibilityInteractionController()
+ .performAccessibilityActionClientThread(accessibilityId, action, interactionId,
+ callback);
+ }
+
+ public void findAccessibilityNodeInfoByViewId(int viewId,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor == null) {
+ return;
+ }
+ getAccessibilityInteractionController()
+ .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback);
+ }
+
+ public void findAccessibilityNodeInfosByViewText(String text, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback) {
+ final ViewAncestor viewAncestor = mViewAncestor.get();
+ if (viewAncestor == null) {
+ return;
+ }
+ getAccessibilityInteractionController()
+ .findAccessibilityNodeInfosByViewTextClientThread(text, interactionId, callback);
+ }
+ }
+
+ /**
+ * Class for managing accessibility interactions initiated from the system
+ * and targeting the view hierarchy. A *ClientThread method is to be
+ * called from the interaction connection this ViewAncestor gives the
+ * system to talk to it and a corresponding *UiThread method that is executed
+ * on the UI thread.
+ */
+ final class AccessibilityInteractionController {
+ private static final int POOL_SIZE = 5;
+
+ private FindByAccessibilitytIdPredicate mFindByAccessibilityIdPredicate =
+ new FindByAccessibilitytIdPredicate();
+
+ private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
+ new ArrayList<AccessibilityNodeInfo>();
+
+ // Reusable poolable arguments for interacting with the view hierarchy
+ // to fit more arguments than Message and to avoid sharing objects between
+ // two messages since several threads can send messages concurrently.
+ private final Pool<SomeArgs> mPool = Pools.synchronizedPool(Pools.finitePool(
+ new PoolableManager<SomeArgs>() {
+ public SomeArgs newInstance() {
+ return new SomeArgs();
+ }
+
+ public void onAcquired(SomeArgs info) {
+ /* do nothing */
+ }
+
+ public void onReleased(SomeArgs info) {
+ info.clear();
+ }
+ }, POOL_SIZE)
+ );
+
+ public class SomeArgs implements Poolable<SomeArgs> {
+ private SomeArgs mNext;
+ private boolean mIsPooled;
+
+ public Object arg1;
+ public Object arg2;
+ public int argi1;
+ public int argi2;
+ public int argi3;
+
+ public SomeArgs getNextPoolable() {
+ return mNext;
+ }
+
+ public boolean isPooled() {
+ return mIsPooled;
+ }
+
+ public void setNextPoolable(SomeArgs args) {
+ mNext = args;
+ }
+
+ public void setPooled(boolean isPooled) {
+ mIsPooled = isPooled;
+ }
+
+ private void clear() {
+ arg1 = null;
+ arg2 = null;
+ argi1 = 0;
+ argi2 = 0;
+ argi3 = 0;
+ }
+ }
+
+ public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+ Message message = Message.obtain();
+ message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
+ message.arg1 = accessibilityId;
+ message.arg2 = interactionId;
+ message.obj = callback;
+ sendMessage(message);
+ }
+
+ public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
+ final int accessibilityId = message.arg1;
+ final int interactionId = message.arg2;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) message.obj;
+
+ View root = ViewAncestor.this.mView;
+ if (root == null) {
+ return;
+ }
+
+ FindByAccessibilitytIdPredicate predicate = mFindByAccessibilityIdPredicate;
+ predicate.init(accessibilityId);
+
+ View target = root.findViewByPredicate(predicate);
+ if (target != null) {
+ AccessibilityNodeInfo info = target.createAccessibilityNodeInfo();
+ try {
+ callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+ }
+
+ public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback) {
+ Message message = Message.obtain();
+ message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
+ message.arg1 = viewId;
+ message.arg2 = interactionId;
+ message.obj = callback;
+ sendMessage(message);
+ }
+
+ public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
+ final int viewId = message.arg1;
+ final int interactionId = message.arg2;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) message.obj;
+
+ View root = ViewAncestor.this.mView;
+ if (root == null) {
+ return;
+ }
+ View target = root.findViewById(viewId);
+ if (target != null) {
+ AccessibilityNodeInfo info = target.createAccessibilityNodeInfo();
+ try {
+ callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+ }
+
+ public void findAccessibilityNodeInfosByViewTextClientThread(String text, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback) {
+ Message message = Message.obtain();
+ message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT;
+ SomeArgs args = mPool.acquire();
+ args.arg1 = text;
+ args.argi1 = interactionId;
+ args.arg2 = callback;
+ message.obj = args;
+ sendMessage(message);
+ }
+
+ public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
+ SomeArgs args = (SomeArgs) message.obj;
+ final String text = (String) args.arg1;
+ final int interactionId = args.argi1;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) args.arg2;
+ mPool.release(args);
+
+ View root = ViewAncestor.this.mView;
+ if (root == null) {
+ return;
+ }
+
+ ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
+ foundViews.clear();
+
+ root.findViewsWithText(foundViews, text);
+ if (foundViews.isEmpty()) {
+ return;
+ }
+
+ List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
+ infos.clear();
+
+ final int viewCount = foundViews.size();
+ for (int i = 0; i < viewCount; i++) {
+ View foundView = foundViews.get(i);
+ infos.add(foundView.createAccessibilityNodeInfo());
+ }
+
+ try {
+ callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+
+ public void performAccessibilityActionClientThread(int accessibilityId, int action,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+ Message message = Message.obtain();
+ message.what = DO_PERFORM_ACCESSIBILITY_ACTION;
+ SomeArgs args = mPool.acquire();
+ args.argi1 = accessibilityId;
+ args.argi2 = action;
+ args.argi3 = interactionId;
+ args.arg1 = callback;
+ message.obj = args;
+ sendMessage(message);
+ }
+
+ public void perfromAccessibilityActionUiThread(Message message) {
+ SomeArgs args = (SomeArgs) message.obj;
+ final int accessibilityId = args.argi1;
+ final int action = args.argi2;
+ final int interactionId = args.argi3;
+ final IAccessibilityInteractionConnectionCallback callback =
+ (IAccessibilityInteractionConnectionCallback) args.arg1;
+ mPool.release(args);
+
+ if (ViewAncestor.this.mView == null) {
+ return;
+ }
+
+ boolean succeeded = false;
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_FOCUS: {
+ succeeded = performActionFocus(accessibilityId);
+ } break;
+ case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
+ succeeded = performActionClearFocus(accessibilityId);
+ } break;
+ case AccessibilityNodeInfo.ACTION_SELECT: {
+ succeeded = performActionSelect(accessibilityId);
+ } break;
+ case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
+ succeeded = performActionClearSelection(accessibilityId);
+ } break;
+ }
+
+ try {
+ callback.setPerformAccessibilityActionResult(succeeded, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+
+ private boolean performActionFocus(int accessibilityId) {
+ View target = findViewByAccessibilityId(accessibilityId);
+ if (target == null) {
+ return false;
+ }
+ // Get out of touch mode since accessibility wants to move focus around.
+ ensureTouchMode(false);
+ return target.requestFocus();
+ }
+
+ private boolean performActionClearFocus(int accessibilityId) {
+ View target = findViewByAccessibilityId(accessibilityId);
+ if (target == null) {
+ return false;
+ }
+ if (!target.isFocused()) {
+ return false;
+ }
+ target.clearFocus();
+ return !target.isFocused();
+ }
+
+ private boolean performActionSelect(int accessibilityId) {
+ View target = findViewByAccessibilityId(accessibilityId);
+ if (target == null) {
+ return false;
+ }
+ if (target.isSelected()) {
+ return false;
+ }
+ target.setSelected(true);
+ return target.isSelected();
+ }
+
+ private boolean performActionClearSelection(int accessibilityId) {
+ View target = findViewByAccessibilityId(accessibilityId);
+ if (target == null) {
+ return false;
+ }
+ if (!target.isSelected()) {
+ return false;
+ }
+ target.setSelected(false);
+ return !target.isSelected();
+ }
+
+ private View findViewByAccessibilityId(int accessibilityId) {
+ View root = ViewAncestor.this.mView;
+ if (root == null) {
+ return null;
+ }
+ mFindByAccessibilityIdPredicate.init(accessibilityId);
+ return root.findViewByPredicate(mFindByAccessibilityIdPredicate);
+ }
+
+ private final class FindByAccessibilitytIdPredicate implements Predicate<View> {
+ public int mSerchedId;
+
+ public void init(int searchedId) {
+ mSerchedId = searchedId;
+ }
+
+ public boolean apply(View view) {
+ return (view.getAccessibilityViewId() == mSerchedId);
+ }
+ }
+ }
+
private static native void nativeShowFPS(Canvas canvas, int durationMillis);
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 36bb046..5919150 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -110,7 +110,21 @@
* double-tap.
*/
private static final int DOUBLE_TAP_TIMEOUT = 300;
-
+
+ /**
+ * Defines the maximum duration in milliseconds between a touch pad
+ * touch and release for a given touch to be considered a tap (click) as
+ * opposed to a hover movement gesture.
+ */
+ private static final int HOVER_TAP_TIMEOUT = 150;
+
+ /**
+ * Defines the maximum distance in pixels that a touch pad touch can move
+ * before being released for it to be considered a tap (click) as opposed
+ * to a hover movement gesture.
+ */
+ private static final int HOVER_TAP_SLOP = 20;
+
/**
* Defines the duration in milliseconds we want to display zoom controls in response
* to a user panning within an application.
@@ -369,7 +383,7 @@
public static int getTapTimeout() {
return TAP_TIMEOUT;
}
-
+
/**
* @return the duration in milliseconds we will wait to see if a touch event
* is a jump tap. If the user does not move within this interval, it is
@@ -387,7 +401,27 @@
public static int getDoubleTapTimeout() {
return DOUBLE_TAP_TIMEOUT;
}
-
+
+ /**
+ * @return the maximum duration in milliseconds between a touch pad
+ * touch and release for a given touch to be considered a tap (click) as
+ * opposed to a hover movement gesture.
+ * @hide
+ */
+ public static int getHoverTapTimeout() {
+ return HOVER_TAP_TIMEOUT;
+ }
+
+ /**
+ * @return the maximum distance in pixels that a touch pad touch can move
+ * before being released for it to be considered a tap (click) as opposed
+ * to a hover movement gesture.
+ * @hide
+ */
+ public static int getHoverTapSlop() {
+ return HOVER_TAP_SLOP;
+ }
+
/**
* @return Inset in pixels to look for touchable content when the user touches the edge of the
* screen
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f504b90..57ee8a0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -35,6 +35,7 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -772,6 +773,18 @@
}
}
+ @Override
+ public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+ final int childrenCount = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = 0; i < childrenCount; i++) {
+ View child = children[i];
+ if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+ child.findViewsWithText(outViews, text);
+ }
+ }
+ }
+
/**
* {@inheritDoc}
*/
@@ -2007,6 +2020,16 @@
return false;
}
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+
+ for (int i = 0, count = mChildrenCount; i < count; i++) {
+ View child = mChildren[i];
+ info.addChild(child);
+ }
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a1ddd08..b0181bb 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -77,8 +77,8 @@
implements Parcelable {
/**
* X position for this window. With the default gravity it is ignored.
- * When using {@link Gravity#LEFT} or {@link Gravity#BEFORE} or {@link Gravity#RIGHT} or
- * {@link Gravity#AFTER} it provides an offset from the given edge.
+ * When using {@link Gravity#LEFT} or {@link Gravity#START} or {@link Gravity#RIGHT} or
+ * {@link Gravity#END} it provides an offset from the given edge.
*/
@ViewDebug.ExportedProperty
public int x;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 7b80797b..32bfa2f 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -16,9 +16,12 @@
package android.view.accessibility;
+import android.accessibilityservice.IAccessibilityServiceConnection;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteException;
import android.text.TextUtils;
+import android.view.View;
import java.util.ArrayList;
@@ -159,6 +162,7 @@
* @see android.accessibilityservice.AccessibilityService
*/
public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
+ private static final boolean DEBUG = false;
/**
* Invalid selection/focus position.
@@ -256,21 +260,38 @@
private static final Object sPoolLock = new Object();
private static AccessibilityEvent sPool;
private static int sPoolSize;
-
private AccessibilityEvent mNext;
private boolean mIsInPool;
private int mEventType;
+ private int mSourceAccessibilityViewId;
+ private int mSourceAccessibilityWindowId;
private CharSequence mPackageName;
private long mEventTime;
private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
+ private IAccessibilityServiceConnection mConnection;
+
/*
* Hide constructor from clients.
*/
private AccessibilityEvent() {
+ }
+ /**
+ * Initialize an event from another one.
+ *
+ * @param event The event to initialize from.
+ */
+ void init(AccessibilityEvent event) {
+ super.init(event);
+ mEventType = event.mEventType;
+ mEventTime = event.mEventTime;
+ mSourceAccessibilityWindowId = event.mSourceAccessibilityWindowId;
+ mSourceAccessibilityViewId = event.mSourceAccessibilityViewId;
+ mPackageName = event.mPackageName;
+ mConnection = event.mConnection;
}
/**
@@ -286,8 +307,11 @@
* Appends an {@link AccessibilityRecord} to the end of event records.
*
* @param record The record to append.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void appendRecord(AccessibilityRecord record) {
+ enforceNotSealed();
mRecords.add(record);
}
@@ -311,11 +335,89 @@
}
/**
+ * Sets the event source.
+ *
+ * @param source The source.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setSource(View source) {
+ enforceNotSealed();
+ if (source != null) {
+ mSourceAccessibilityWindowId = source.getAccessibilityWindowId();
+ mSourceAccessibilityViewId = source.getAccessibilityViewId();
+ } else {
+ mSourceAccessibilityWindowId = View.NO_ID;
+ mSourceAccessibilityViewId = View.NO_ID;
+ }
+ }
+
+ /**
+ * Gets the {@link AccessibilityNodeInfo} of the event source.
+ * <p>
+ * <strong>
+ * It is a client responsibility to recycle the received info by
+ * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+ * of multiple instances.
+ * </strong>
+ * </p>
+ * @return The info.
+ */
+ public AccessibilityNodeInfo getSource() {
+ enforceSealed();
+ if (mSourceAccessibilityWindowId == View.NO_ID
+ || mSourceAccessibilityViewId == View.NO_ID) {
+ return null;
+ }
+ try {
+ return mConnection.findAccessibilityNodeInfoByAccessibilityId(
+ mSourceAccessibilityWindowId, mSourceAccessibilityViewId);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the id of the window from which the event comes from.
+ *
+ * @return The window id.
+ */
+ public int getAccessibilityWindowId() {
+ return mSourceAccessibilityWindowId;
+ }
+
+ /**
+ * Sets the client token for the accessibility service that
+ * provided this node info.
+ *
+ * @param connection The connection.
+ *
+ * @hide
+ */
+ public final void setConnection(IAccessibilityServiceConnection connection) {
+ mConnection = connection;
+ }
+
+ /**
+ * Gets the accessibility window id of the source window.
+ *
+ * @return The id.
+ *
+ * @hide
+ */
+ public int getSourceAccessibilityWindowId() {
+ return mSourceAccessibilityWindowId;
+ }
+
+ /**
* Sets the event type.
*
* @param eventType The event type.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setEventType(int eventType) {
+ enforceNotSealed();
mEventType = eventType;
}
@@ -332,8 +434,11 @@
* Sets the time in which this event was sent.
*
* @param eventTime The event time.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setEventTime(long eventTime) {
+ enforceNotSealed();
mEventTime = eventTime;
}
@@ -350,8 +455,11 @@
* Sets the package name of the source.
*
* @param packageName The package name.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setPackageName(CharSequence packageName) {
+ enforceNotSealed();
mPackageName = packageName;
}
@@ -370,6 +478,27 @@
/**
* Returns a cached instance if such is available or a new one is
+ * instantiated with type property set.
+ *
+ * @param event The other event.
+ * @return An instance.
+ */
+ public static AccessibilityEvent obtain(AccessibilityEvent event) {
+ AccessibilityEvent eventClone = AccessibilityEvent.obtain();
+ eventClone.init(event);
+
+ final int recordCount = event.mRecords.size();
+ for (int i = 0; i < recordCount; i++) {
+ AccessibilityRecord record = event.mRecords.get(i);
+ AccessibilityRecord recordClone = AccessibilityRecord.obtain(record);
+ eventClone.mRecords.add(recordClone);
+ }
+
+ return eventClone;
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
* instantiated.
*
* @return An instance.
@@ -413,11 +542,16 @@
/**
* Clears the state of this instance.
+ *
+ * @hide
*/
@Override
protected void clear() {
super.clear();
+ mConnection = null;
mEventType = 0;
+ mSourceAccessibilityViewId = View.NO_ID;
+ mSourceAccessibilityWindowId = View.NO_ID;
mPackageName = null;
mEventTime = 0;
while (!mRecords.isEmpty()) {
@@ -432,7 +566,14 @@
* @param parcel A parcel containing the state of a {@link AccessibilityEvent}.
*/
public void initFromParcel(Parcel parcel) {
+ if (parcel.readInt() == 1) {
+ mConnection = IAccessibilityServiceConnection.Stub.asInterface(
+ parcel.readStrongBinder());
+ }
+ setSealed(parcel.readInt() == 1);
mEventType = parcel.readInt();
+ mSourceAccessibilityWindowId = parcel.readInt();
+ mSourceAccessibilityViewId = parcel.readInt();
mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
mEventTime = parcel.readLong();
readAccessibilityRecordFromParcel(this, parcel);
@@ -471,7 +612,16 @@
* {@inheritDoc}
*/
public void writeToParcel(Parcel parcel, int flags) {
+ if (mConnection == null) {
+ parcel.writeInt(0);
+ } else {
+ parcel.writeInt(1);
+ parcel.writeStrongBinder(mConnection.asBinder());
+ }
+ parcel.writeInt(isSealed() ? 1 : 0);
parcel.writeInt(mEventType);
+ parcel.writeInt(mSourceAccessibilityWindowId);
+ parcel.writeInt(mSourceAccessibilityViewId);
TextUtils.writeToParcel(mPackageName, parcel, 0);
parcel.writeLong(mEventTime);
writeAccessibilityRecordToParcel(this, parcel, flags);
@@ -519,18 +669,36 @@
builder.append("; EventType: ").append(eventTypeToString(mEventType));
builder.append("; EventTime: ").append(mEventTime);
builder.append("; PackageName: ").append(mPackageName);
- builder.append(" \n{\n");
builder.append(super.toString());
- builder.append("\n");
- for (int i = 0; i < mRecords.size(); i++) {
- AccessibilityRecord record = mRecords.get(i);
- builder.append(" Record ");
- builder.append(i);
- builder.append(":");
- builder.append(record.toString());
+ if (DEBUG) {
builder.append("\n");
+ builder.append("; sourceAccessibilityWindowId: ").append(mSourceAccessibilityWindowId);
+ builder.append("; sourceAccessibilityViewId: ").append(mSourceAccessibilityViewId);
+ for (int i = 0; i < mRecords.size(); i++) {
+ AccessibilityRecord record = mRecords.get(i);
+ builder.append(" Record ");
+ builder.append(i);
+ builder.append(":");
+ builder.append(" [ ClassName: " + record.mClassName);
+ builder.append("; Text: " + record.mText);
+ builder.append("; ContentDescription: " + record.mContentDescription);
+ builder.append("; ItemCount: " + record.mItemCount);
+ builder.append("; CurrentItemIndex: " + record.mCurrentItemIndex);
+ builder.append("; IsEnabled: " + record.isEnabled());
+ builder.append("; IsPassword: " + record.isPassword());
+ builder.append("; IsChecked: " + record.isChecked());
+ builder.append("; IsFullScreen: " + record.isFullScreen());
+ builder.append("; BeforeText: " + record.mBeforeText);
+ builder.append("; FromIndex: " + record.mFromIndex);
+ builder.append("; AddedCount: " + record.mAddedCount);
+ builder.append("; RemovedCount: " + record.mRemovedCount);
+ builder.append("; ParcelableData: " + record.mParcelableData);
+ builder.append(" ]");
+ builder.append("\n");
+ }
+ } else {
+ builder.append("; recordCount: ").append(getAddedCount());
}
- builder.append("}\n");
return builder.toString();
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 88f8878..eece64a 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -28,10 +28,13 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.util.Log;
+import android.view.IWindow;
+import android.view.View;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
@@ -62,6 +65,21 @@
boolean mIsEnabled;
+ final CopyOnWriteArrayList<AccessibilityStateChangeListener> mAccessibilityStateChangeListeners =
+ new CopyOnWriteArrayList<AccessibilityStateChangeListener>();
+
+ /**
+ * Listener for the accessibility state.
+ */
+ public interface AccessibilityStateChangeListener {
+ /**
+ * Called back on change in the accessibility state.
+ *
+ * @param enabled
+ */
+ public void onAccessibilityStateChanged(boolean enabled);
+ }
+
final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
public void setEnabled(boolean enabled) {
mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget();
@@ -78,9 +96,8 @@
public void handleMessage(Message message) {
switch (message.what) {
case DO_SET_ENABLED :
- synchronized (mHandler) {
- mIsEnabled = (message.arg1 == 1);
- }
+ final boolean isEnabled = (message.arg1 == 1);
+ setAccessibilityState(isEnabled);
return;
default :
Log.w(LOG_TAG, "Unknown message type: " + message.what);
@@ -117,7 +134,8 @@
mService = service;
try {
- mIsEnabled = mService.addClient(mClient);
+ final boolean isEnabled = mService.addClient(mClient);
+ setAccessibilityState(isEnabled);
} catch (RemoteException re) {
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
}
@@ -253,4 +271,81 @@
}
return Collections.unmodifiableList(services);
}
+
+ /**
+ * Registers an {@link AccessibilityStateChangeListener}.
+ *
+ * @param listener The listener.
+ * @return True if successfully registered.
+ */
+ public boolean addAccessibilityStateChangeListener(
+ AccessibilityStateChangeListener listener) {
+ return mAccessibilityStateChangeListeners.add(listener);
+ }
+
+ /**
+ * Unregisters an {@link AccessibilityStateChangeListener}.
+ *
+ * @param listener The listener.
+ * @return True if successfully unregistered.
+ */
+ public boolean removeAccessibilityStateChangeListener(
+ AccessibilityStateChangeListener listener) {
+ return mAccessibilityStateChangeListeners.remove(listener);
+ }
+
+ /**
+ * Sets the enabled state.
+ *
+ * @param isEnabled The accessibility state.
+ */
+ private void setAccessibilityState(boolean isEnabled) {
+ synchronized (mHandler) {
+ if (isEnabled != mIsEnabled) {
+ mIsEnabled = isEnabled;
+ notifyAccessibilityStateChanged();
+ }
+ }
+ }
+
+ /**
+ * Notifies the registered {@link AccessibilityStateChangeListener}s.
+ */
+ private void notifyAccessibilityStateChanged() {
+ final int listenerCount = mAccessibilityStateChangeListeners.size();
+ for (int i = 0; i < listenerCount; i++) {
+ mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled);
+ }
+ }
+
+ /**
+ * Adds an accessibility interaction connection interface for a given window.
+ * @param windowToken The window token to which a connection is added.
+ * @param connection The connection.
+ *
+ * @hide
+ */
+ public int addAccessibilityInteractionConnection(IWindow windowToken,
+ IAccessibilityInteractionConnection connection) {
+ try {
+ return mService.addAccessibilityInteractionConnection(windowToken, connection);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
+ }
+ return View.NO_ID;
+ }
+
+ /**
+ * Removed an accessibility interaction connection interface for a given window.
+ * @param windowToken The window token to which a connection is removed.
+ *
+ * @hide
+ */
+ public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+ try {
+ mService.removeAccessibilityInteractionConnection(windowToken);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl b/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl
new file mode 100644
index 0000000..59175ce
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+parcelable AccessibilityNodeInfo;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
new file mode 100644
index 0000000..752f864
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -0,0 +1,947 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.View;
+
+/**
+ * This class represents a node of the screen content. From the point of
+ * view of an accessibility service the screen content is presented as tree
+ * of accessibility nodes.
+ *
+ * TODO(svertoslavganov): Update the documentation, add sample, and describe
+ * the security policy.
+ */
+public class AccessibilityNodeInfo implements Parcelable {
+
+ private static final boolean DEBUG = false;
+
+ // Actions.
+
+ /**
+ * Action that focuses the node.
+ */
+ public static final int ACTION_FOCUS = 0x00000001;
+
+ /**
+ * Action that unfocuses the node.
+ */
+ public static final int ACTION_CLEAR_FOCUS = 0x00000002;
+
+ /**
+ * Action that selects the node.
+ */
+ public static final int ACTION_SELECT = 0x00000004;
+
+ /**
+ * Action that unselects the node.
+ */
+ public static final int ACTION_CLEAR_SELECTION = 0x00000008;
+
+ // Boolean attributes.
+
+ private static final int PROPERTY_CHECKABLE = 0x00000001;
+
+ private static final int PROPERTY_CHECKED = 0x00000002;
+
+ private static final int PROPERTY_FOCUSABLE = 0x00000004;
+
+ private static final int PROPERTY_FOCUSED = 0x00000008;
+
+ private static final int PROPERTY_SELECTED = 0x00000010;
+
+ private static final int PROPERTY_CLICKABLE = 0x00000020;
+
+ private static final int PROPERTY_LONG_CLICKABLE = 0x00000040;
+
+ private static final int PROPERTY_ENABLED = 0x00000080;
+
+ private static final int PROPERTY_PASSWORD = 0x00000100;
+
+ // Readable representations - lazily initialized.
+ private static SparseArray<String> sActionSymbolicNames;
+
+ // Housekeeping.
+ private static final int MAX_POOL_SIZE = 50;
+ private static final Object sPoolLock = new Object();
+ private static AccessibilityNodeInfo sPool;
+ private static int sPoolSize;
+ private AccessibilityNodeInfo mNext;
+ private boolean mIsInPool;
+ private boolean mSealed;
+
+ // Data.
+ private int mAccessibilityViewId;
+ private int mAccessibilityWindowId;
+ private int mParentAccessibilityViewId;
+ private int mBooleanProperties;
+ private final Rect mBounds = new Rect();
+
+ private CharSequence mPackageName;
+ private CharSequence mClassName;
+ private CharSequence mText;
+ private CharSequence mContentDescription;
+
+ private final SparseIntArray mChildAccessibilityIds = new SparseIntArray();
+ private int mActions;
+
+ private IAccessibilityServiceConnection mConnection;
+
+ /**
+ * Hide constructor from clients.
+ */
+ private AccessibilityNodeInfo() {
+ /* do nothing */
+ }
+
+ /**
+ * Sets the source.
+ *
+ * @param source The info source.
+ */
+ public void setSource(View source) {
+ enforceNotSealed();
+ mAccessibilityViewId = source.getAccessibilityViewId();
+ mAccessibilityWindowId = source.getAccessibilityWindowId();
+ }
+
+ /**
+ * Gets the id of the window from which the info comes from.
+ *
+ * @return The window id.
+ */
+ public int getAccessibilityWindowId() {
+ return mAccessibilityWindowId;
+ }
+
+ /**
+ * Gets the number of children.
+ *
+ * @return The child count.
+ */
+ public int getChildCount() {
+ return mChildAccessibilityIds.size();
+ }
+
+ /**
+ * Get the child at given index.
+ * <p>
+ * <strong>
+ * It is a client responsibility to recycle the received info by
+ * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+ * of multiple instances.
+ * </strong>
+ * </p>
+ * @param index The child index.
+ * @return The child node.
+ *
+ * @throws IllegalStateException If called outside of an AccessibilityService.
+ *
+ */
+ public AccessibilityNodeInfo getChild(int index) {
+ enforceSealed();
+ final int childAccessibilityViewId = mChildAccessibilityIds.get(index);
+ try {
+ return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
+ childAccessibilityViewId);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Adds a child.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param child The child.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void addChild(View child) {
+ enforceNotSealed();
+ final int childAccessibilityViewId = child.getAccessibilityViewId();
+ final int index = mChildAccessibilityIds.size();
+ mChildAccessibilityIds.put(index, childAccessibilityViewId);
+ }
+
+ /**
+ * Gets the actions that can be performed on the node.
+ *
+ * @return The bit mask of with actions.
+ *
+ * @see AccessibilityNodeInfo#ACTION_FOCUS
+ * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
+ * @see AccessibilityNodeInfo#ACTION_SELECT
+ * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
+ */
+ public int getActions() {
+ return mActions;
+ }
+
+ /**
+ * Adds an action that can be performed on the node.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param action The action.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void addAction(int action) {
+ enforceNotSealed();
+ mActions |= action;
+ }
+
+ /**
+ * Performs an action on the node.
+ * <p>
+ * Note: An action can be performed only if the request is made
+ * from an {@link android.accessibilityservice.AccessibilityService}.
+ * </p>
+ * @param action The action to perform.
+ * @return True if the action was performed.
+ *
+ * @throws IllegalStateException If called outside of an AccessibilityService.
+ */
+ public boolean performAction(int action) {
+ enforceSealed();
+ try {
+ return mConnection.performAccessibilityAction(mAccessibilityWindowId,
+ mAccessibilityViewId, action);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the unique id identifying this node's parent.
+ * <p>
+ * <strong>
+ * It is a client responsibility to recycle the received info by
+ * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+ * of multiple instances.
+ * </strong>
+ * </p>
+ * @return The node's patent id.
+ */
+ public AccessibilityNodeInfo getParent() {
+ enforceSealed();
+ try {
+ return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
+ mParentAccessibilityViewId);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the parent.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param parent The parent.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setParent(View parent) {
+ enforceNotSealed();
+ mParentAccessibilityViewId = parent.getAccessibilityViewId();
+ }
+
+ /**
+ * Gets the node bounds in parent coordinates.
+ *
+ * @param outBounds The output node bounds.
+ */
+ public void getBounds(Rect outBounds) {
+ outBounds.set(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
+ }
+
+ /**
+ * Sets the node bounds in parent coordinates.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param bounds The node bounds.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setBounds(Rect bounds) {
+ enforceNotSealed();
+ mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
+
+ /**
+ * Gets whether this node is checkable.
+ *
+ * @return True if the node is checkable.
+ */
+ public boolean isCheckable() {
+ return getBooleanProperty(PROPERTY_CHECKABLE);
+ }
+
+ /**
+ * Sets whether this node is checkable.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param checkable True if the node is checkable.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setCheckable(boolean checkable) {
+ setBooleanProperty(PROPERTY_CHECKABLE, checkable);
+ }
+
+ /**
+ * Gets whether this node is checked.
+ *
+ * @return True if the node is checked.
+ */
+ public boolean isChecked() {
+ return getBooleanProperty(PROPERTY_CHECKED);
+ }
+
+ /**
+ * Sets whether this node is checked.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param checked True if the node is checked.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setChecked(boolean checked) {
+ setBooleanProperty(PROPERTY_CHECKED, checked);
+ }
+
+ /**
+ * Gets whether this node is focusable.
+ *
+ * @return True if the node is focusable.
+ */
+ public boolean isFocusable() {
+ return getBooleanProperty(PROPERTY_FOCUSABLE);
+ }
+
+ /**
+ * Sets whether this node is focusable.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param focusable True if the node is focusable.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setFocusable(boolean focusable) {
+ setBooleanProperty(PROPERTY_FOCUSABLE, focusable);
+ }
+
+ /**
+ * Gets whether this node is focused.
+ *
+ * @return True if the node is focused.
+ */
+ public boolean isFocused() {
+ return getBooleanProperty(PROPERTY_FOCUSED);
+ }
+
+ /**
+ * Sets whether this node is focused.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param focused True if the node is focused.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setFocused(boolean focused) {
+ setBooleanProperty(PROPERTY_FOCUSED, focused);
+ }
+
+ /**
+ * Gets whether this node is selected.
+ *
+ * @return True if the node is selected.
+ */
+ public boolean isSelected() {
+ return getBooleanProperty(PROPERTY_SELECTED);
+ }
+
+ /**
+ * Sets whether this node is selected.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param selected True if the node is selected.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setSelected(boolean selected) {
+ setBooleanProperty(PROPERTY_SELECTED, selected);
+ }
+
+ /**
+ * Gets whether this node is clickable.
+ *
+ * @return True if the node is clickable.
+ */
+ public boolean isClickable() {
+ return getBooleanProperty(PROPERTY_CLICKABLE);
+ }
+
+ /**
+ * Sets whether this node is clickable.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param clickable True if the node is clickable.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setClickable(boolean clickable) {
+ setBooleanProperty(PROPERTY_CLICKABLE, clickable);
+ }
+
+ /**
+ * Gets whether this node is long clickable.
+ *
+ * @return True if the node is long clickable.
+ */
+ public boolean isLongClickable() {
+ return getBooleanProperty(PROPERTY_LONG_CLICKABLE);
+ }
+
+ /**
+ * Sets whether this node is long clickable.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param longClickable True if the node is long clickable.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setLongClickable(boolean longClickable) {
+ setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable);
+ }
+
+ /**
+ * Gets whether this node is enabled.
+ *
+ * @return True if the node is enabled.
+ */
+ public boolean isEnabled() {
+ return getBooleanProperty(PROPERTY_ENABLED);
+ }
+
+ /**
+ * Sets whether this node is enabled.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param enabled True if the node is enabled.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setEnabled(boolean enabled) {
+ setBooleanProperty(PROPERTY_ENABLED, enabled);
+ }
+
+ /**
+ * Gets whether this node is a password.
+ *
+ * @return True if the node is a password.
+ */
+ public boolean isPassword() {
+ return getBooleanProperty(PROPERTY_PASSWORD);
+ }
+
+ /**
+ * Sets whether this node is a password.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param password True if the node is a password.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setPassword(boolean password) {
+ setBooleanProperty(PROPERTY_PASSWORD, password);
+ }
+
+ /**
+ * Gets the package this node comes from.
+ *
+ * @return The package name.
+ */
+ public CharSequence getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Sets the package this node comes from.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param packageName The package name.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setPackageName(CharSequence packageName) {
+ enforceNotSealed();
+ mPackageName = packageName;
+ }
+
+ /**
+ * Gets the class this node comes from.
+ *
+ * @return The class name.
+ */
+ public CharSequence getClassName() {
+ return mClassName;
+ }
+
+ /**
+ * Sets the class this node comes from.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param className The class name.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setClassName(CharSequence className) {
+ enforceNotSealed();
+ mClassName = className;
+ }
+
+ /**
+ * Gets the text of this node.
+ *
+ * @return The text.
+ */
+ public CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Sets the text of this node.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param text The text.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setText(CharSequence text) {
+ enforceNotSealed();
+ mText = text;
+ }
+
+ /**
+ * Gets the content description of this node.
+ *
+ * @return The content description.
+ */
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * Sets the content description of this node.
+ * <p>
+ * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ * @param contentDescription The content description.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setContentDescription(CharSequence contentDescription) {
+ enforceNotSealed();
+ mContentDescription = contentDescription;
+ }
+
+ /**
+ * Gets the value of a boolean property.
+ *
+ * @param property The property.
+ * @return The value.
+ */
+ private boolean getBooleanProperty(int property) {
+ return (mBooleanProperties & property) != 0;
+ }
+
+ /**
+ * Sets a boolean property.
+ *
+ * @param property The property.
+ * @param value The value.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ private void setBooleanProperty(int property, boolean value) {
+ enforceNotSealed();
+ if (value) {
+ mBooleanProperties |= property;
+ } else {
+ mBooleanProperties &= ~property;
+ }
+ }
+
+ /**
+ * Sets the connection for interacting with the system.
+ *
+ * @param connection The client token.
+ *
+ * @hide
+ */
+ public final void setConnection(IAccessibilityServiceConnection connection) {
+ mConnection = connection;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Sets if this instance is sealed.
+ *
+ * @param sealed Whether is sealed.
+ *
+ * @hide
+ */
+ public void setSealed(boolean sealed) {
+ mSealed = sealed;
+ }
+
+ /**
+ * Gets if this instance is sealed.
+ *
+ * @return Whether is sealed.
+ *
+ * @hide
+ */
+ public boolean isSealed() {
+ return mSealed;
+ }
+
+ /**
+ * Enforces that this instance is sealed.
+ *
+ * @throws IllegalStateException If this instance is not sealed.
+ *
+ * @hide
+ */
+ protected void enforceSealed() {
+ if (!isSealed()) {
+ throw new IllegalStateException("Cannot perform this "
+ + "action on a not sealed instance.");
+ }
+ }
+
+ /**
+ * Enforces that this instance is not sealed.
+ *
+ * @throws IllegalStateException If this instance is sealed.
+ *
+ * @hide
+ */
+ protected void enforceNotSealed() {
+ if (isSealed()) {
+ throw new IllegalStateException("Cannot perform this "
+ + "action on an sealed instance.");
+ }
+ }
+
+ /**
+ * Returns a cached instance if such is available otherwise a new one
+ * and sets the source.
+ *
+ * @return An instance.
+ *
+ * @see #setSource(View)
+ */
+ public static AccessibilityNodeInfo obtain(View source) {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ info.setSource(source);
+ return info;
+ }
+
+ /**
+ * Returns a cached instance if such is available otherwise a new one.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityNodeInfo obtain() {
+ synchronized (sPoolLock) {
+ if (sPool != null) {
+ AccessibilityNodeInfo info = sPool;
+ sPool = sPool.mNext;
+ sPoolSize--;
+ info.mNext = null;
+ info.mIsInPool = false;
+ }
+ return new AccessibilityNodeInfo();
+ }
+ }
+
+ /**
+ * Return an instance back to be reused.
+ * <p>
+ * <b>Note: You must not touch the object after calling this function.</b>
+ *
+ * @throws IllegalStateException If the info is already recycled.
+ */
+ public void recycle() {
+ if (mIsInPool) {
+ throw new IllegalStateException("Info already recycled!");
+ }
+ clear();
+ synchronized (sPoolLock) {
+ if (sPoolSize <= MAX_POOL_SIZE) {
+ mNext = sPool;
+ sPool = this;
+ mIsInPool = true;
+ sPoolSize++;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * <b>Note: After the instance is written to a parcel it is recycled.
+ * You must not touch the object after calling this function.</b>
+ * </p>
+ */
+ public void writeToParcel(Parcel parcel, int flags) {
+ if (mConnection == null) {
+ parcel.writeInt(0);
+ } else {
+ parcel.writeInt(1);
+ parcel.writeStrongBinder(mConnection.asBinder());
+ }
+ parcel.writeInt(isSealed() ? 1 : 0);
+ parcel.writeInt(mAccessibilityViewId);
+ parcel.writeInt(mAccessibilityWindowId);
+ parcel.writeInt(mParentAccessibilityViewId);
+
+ SparseIntArray childIds = mChildAccessibilityIds;
+ final int childIdsSize = childIds.size();
+ parcel.writeInt(childIdsSize);
+ for (int i = 0; i < childIdsSize; i++) {
+ parcel.writeInt(childIds.valueAt(i));
+ }
+
+ parcel.writeInt(mBounds.top);
+ parcel.writeInt(mBounds.bottom);
+ parcel.writeInt(mBounds.left);
+ parcel.writeInt(mBounds.right);
+
+ parcel.writeInt(mActions);
+
+ parcel.writeInt(mBooleanProperties);
+
+ TextUtils.writeToParcel(mPackageName, parcel, flags);
+ TextUtils.writeToParcel(mClassName, parcel, flags);
+ TextUtils.writeToParcel(mText, parcel, flags);
+ TextUtils.writeToParcel(mContentDescription, parcel, flags);
+
+ // Since instances of this class are fetched via synchronous i.e. blocking
+ // calls in IPCs and we always recycle as soon as the instance is marshaled.
+ recycle();
+ }
+
+ /**
+ * Creates a new instance from a {@link Parcel}.
+ *
+ * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
+ */
+ private void initFromParcel(Parcel parcel) {
+ if (parcel.readInt() == 1) {
+ mConnection = IAccessibilityServiceConnection.Stub.asInterface(
+ parcel.readStrongBinder());
+ }
+ mSealed = (parcel.readInt() == 1);
+ mAccessibilityViewId = parcel.readInt();
+ mAccessibilityWindowId = parcel.readInt();
+ mParentAccessibilityViewId = parcel.readInt();
+
+ SparseIntArray childIds = mChildAccessibilityIds;
+ final int childrenSize = parcel.readInt();
+ for (int i = 0; i < childrenSize; i++) {
+ final int childId = parcel.readInt();
+ childIds.put(i, childId);
+ }
+
+ mBounds.top = parcel.readInt();
+ mBounds.bottom = parcel.readInt();
+ mBounds.left = parcel.readInt();
+ mBounds.right = parcel.readInt();
+
+ mActions = parcel.readInt();
+
+ mBooleanProperties = parcel.readInt();
+
+ mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ }
+
+ /**
+ * Clears the state of this instance.
+ */
+ private void clear() {
+ mSealed = false;
+ mConnection = null;
+ mAccessibilityViewId = View.NO_ID;
+ mParentAccessibilityViewId = View.NO_ID;
+ mChildAccessibilityIds.clear();
+ mBounds.set(0, 0, 0, 0);
+ mBooleanProperties = 0;
+ mPackageName = null;
+ mClassName = null;
+ mText = null;
+ mContentDescription = null;
+ mActions = 0;
+ }
+
+ /**
+ * Gets the human readable action symbolic name.
+ *
+ * @param action The action.
+ * @return The symbolic name.
+ */
+ private static String getActionSymbolicName(int action) {
+ SparseArray<String> actionSymbolicNames = sActionSymbolicNames;
+ if (actionSymbolicNames == null) {
+ actionSymbolicNames = sActionSymbolicNames = new SparseArray<String>();
+ actionSymbolicNames.put(ACTION_FOCUS, "ACTION_FOCUS");
+ actionSymbolicNames.put(ACTION_CLEAR_FOCUS, "ACTION_UNFOCUS");
+ actionSymbolicNames.put(ACTION_SELECT, "ACTION_SELECT");
+ actionSymbolicNames.put(ACTION_CLEAR_SELECTION, "ACTION_UNSELECT");
+ }
+ return actionSymbolicNames.get(action);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mAccessibilityViewId;
+ result = prime * result + mAccessibilityWindowId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(super.toString());
+
+ if (DEBUG) {
+ builder.append("; accessibilityId: " + mAccessibilityViewId);
+ builder.append("; parentAccessibilityId: " + mParentAccessibilityViewId);
+ SparseIntArray childIds = mChildAccessibilityIds;
+ builder.append("; childAccessibilityIds: [");
+ for (int i = 0, count = childIds.size(); i < count; i++) {
+ builder.append(childIds.valueAt(i));
+ if (i < count - 1) {
+ builder.append(", ");
+ }
+ }
+ builder.append("]");
+ }
+
+ builder.append("; bounds: " + mBounds);
+
+ builder.append("; packageName: ").append(mPackageName);
+ builder.append("; className: ").append(mClassName);
+ builder.append("; text: ").append(mText);
+ builder.append("; contentDescription: ").append(mContentDescription);
+
+ builder.append("; checkable: ").append(isCheckable());
+ builder.append("; checked: ").append(isChecked());
+ builder.append("; focusable: ").append(isFocusable());
+ builder.append("; focused: ").append(isFocused());
+ builder.append("; selected: ").append(isSelected());
+ builder.append("; clickable: ").append(isClickable());
+ builder.append("; longClickable: ").append(isLongClickable());
+ builder.append("; enabled: ").append(isEnabled());
+ builder.append("; password: ").append(isPassword());
+
+ builder.append("; [");
+
+ for (int actionBits = mActions; actionBits != 0;) {
+ final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
+ actionBits &= ~action;
+ builder.append(getActionSymbolicName(action));
+ if (actionBits != 0) {
+ builder.append(", ");
+ }
+ }
+
+ builder.append("]");
+
+ return builder.toString();
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
+ new Parcelable.Creator<AccessibilityNodeInfo>() {
+ public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ info.initFromParcel(parcel);
+ return info;
+ }
+
+ public AccessibilityNodeInfo[] newArray(int size) {
+ return new AccessibilityNodeInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 7819b17..4bf03a7 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -38,13 +38,14 @@
private static final int PROPERTY_PASSWORD = 0x00000004;
private static final int PROPERTY_FULL_SCREEN = 0x00000080;
+ // Housekeeping
private static final int MAX_POOL_SIZE = 10;
private static final Object sPoolLock = new Object();
private static AccessibilityRecord sPool;
private static int sPoolSize;
-
private AccessibilityRecord mNext;
private boolean mIsInPool;
+ private boolean mSealed;
protected int mBooleanProperties;
protected int mCurrentItemIndex;
@@ -68,6 +69,26 @@
}
/**
+ * Initialize this record from another one.
+ *
+ * @param record The to initialize from.
+ */
+ void init(AccessibilityRecord record) {
+ mSealed = record.isSealed();
+ mBooleanProperties = record.mBooleanProperties;
+ mCurrentItemIndex = record.mCurrentItemIndex;
+ mItemCount = record.mItemCount;
+ mFromIndex = record.mFromIndex;
+ mAddedCount = record.mAddedCount;
+ mRemovedCount = record.mRemovedCount;
+ mClassName = record.mClassName;
+ mContentDescription = record.mContentDescription;
+ mBeforeText = record.mBeforeText;
+ mParcelableData = record.mParcelableData;
+ mText.addAll(record.mText);
+ }
+
+ /**
* Gets if the source is checked.
*
* @return True if the view is checked, false otherwise.
@@ -80,8 +101,11 @@
* Sets if the source is checked.
*
* @param isChecked True if the view is checked, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setChecked(boolean isChecked) {
+ enforceNotSealed();
setBooleanProperty(PROPERTY_CHECKED, isChecked);
}
@@ -98,8 +122,11 @@
* Sets if the source is enabled.
*
* @param isEnabled True if the view is enabled, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setEnabled(boolean isEnabled) {
+ enforceNotSealed();
setBooleanProperty(PROPERTY_ENABLED, isEnabled);
}
@@ -116,21 +143,15 @@
* Sets if the source is a password field.
*
* @param isPassword True if the view is a password field, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setPassword(boolean isPassword) {
+ enforceNotSealed();
setBooleanProperty(PROPERTY_PASSWORD, isPassword);
}
/**
- * Sets if the source is taking the entire screen.
- *
- * @param isFullScreen True if the source is full screen, false otherwise.
- */
- public void setFullScreen(boolean isFullScreen) {
- setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
- }
-
- /**
* Gets if the source is taking the entire screen.
*
* @return True if the source is full screen, false otherwise.
@@ -140,6 +161,18 @@
}
/**
+ * Sets if the source is taking the entire screen.
+ *
+ * @param isFullScreen True if the source is full screen, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setFullScreen(boolean isFullScreen) {
+ enforceNotSealed();
+ setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
+ }
+
+ /**
* Gets the number of items that can be visited.
*
* @return The number of items.
@@ -152,8 +185,11 @@
* Sets the number of items that can be visited.
*
* @param itemCount The number of items.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setItemCount(int itemCount) {
+ enforceNotSealed();
mItemCount = itemCount;
}
@@ -170,8 +206,11 @@
* Sets the index of the source in the list of items that can be visited.
*
* @param currentItemIndex The current item index.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setCurrentItemIndex(int currentItemIndex) {
+ enforceNotSealed();
mCurrentItemIndex = currentItemIndex;
}
@@ -188,8 +227,11 @@
* Sets the index of the first character of the changed sequence.
*
* @param fromIndex The index of the first character.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setFromIndex(int fromIndex) {
+ enforceNotSealed();
mFromIndex = fromIndex;
}
@@ -206,8 +248,11 @@
* Sets the number of added characters.
*
* @param addedCount The number of added characters.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setAddedCount(int addedCount) {
+ enforceNotSealed();
mAddedCount = addedCount;
}
@@ -224,8 +269,11 @@
* Sets the number of removed characters.
*
* @param removedCount The number of removed characters.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setRemovedCount(int removedCount) {
+ enforceNotSealed();
mRemovedCount = removedCount;
}
@@ -242,8 +290,11 @@
* Sets the class name of the source.
*
* @param className The lass name.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setClassName(CharSequence className) {
+ enforceNotSealed();
mClassName = className;
}
@@ -270,8 +321,11 @@
* Sets the text before a change.
*
* @param beforeText The text before the change.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setBeforeText(CharSequence beforeText) {
+ enforceNotSealed();
mBeforeText = beforeText;
}
@@ -288,8 +342,11 @@
* Sets the description of the source.
*
* @param contentDescription The description.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setContentDescription(CharSequence contentDescription) {
+ enforceNotSealed();
mContentDescription = contentDescription;
}
@@ -306,18 +363,71 @@
* Sets the {@link Parcelable} data of the event.
*
* @param parcelableData The parcelable data.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
*/
public void setParcelableData(Parcelable parcelableData) {
+ enforceNotSealed();
mParcelableData = parcelableData;
}
/**
+ * Sets if this instance is sealed.
+ *
+ * @param sealed Whether is sealed.
+ *
+ * @hide
+ */
+ public void setSealed(boolean sealed) {
+ mSealed = sealed;
+ }
+
+ /**
+ * Gets if this instance is sealed.
+ *
+ * @return Whether is sealed.
+ *
+ * @hide
+ */
+ public boolean isSealed() {
+ return mSealed;
+ }
+
+ /**
+ * Enforces that this instance is sealed.
+ *
+ * @throws IllegalStateException If this instance is not sealed.
+ *
+ * @hide
+ */
+ protected void enforceSealed() {
+ if (!isSealed()) {
+ throw new IllegalStateException("Cannot perform this "
+ + "action on a not sealed instance.");
+ }
+ }
+
+ /**
+ * Enforces that this instance is not sealed.
+ *
+ * @throws IllegalStateException If this instance is sealed.
+ *
+ * @hide
+ */
+ protected void enforceNotSealed() {
+ if (isSealed()) {
+ throw new IllegalStateException("Cannot perform this "
+ + "action on an sealed instance.");
+ }
+ }
+
+ /**
* Gets the value of a boolean property.
*
* @param property The property.
* @return The value.
*/
- public boolean getBooleanProperty(int property) {
+ private boolean getBooleanProperty(int property) {
return (mBooleanProperties & property) == property;
}
@@ -337,6 +447,19 @@
/**
* Returns a cached instance if such is available or a new one is
+ * instantiated. The instance is initialized with data from the
+ * given record.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityRecord obtain(AccessibilityRecord record) {
+ AccessibilityRecord clone = AccessibilityRecord.obtain();
+ clone.init(record);
+ return clone;
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
* instantiated.
*
* @return An instance.
@@ -379,8 +502,11 @@
/**
* Clears the state of this instance.
+ *
+ * @hide
*/
protected void clear() {
+ mSealed = false;
mBooleanProperties = 0;
mCurrentItemIndex = INVALID_POSITION;
mItemCount = 0;
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
new file mode 100644
index 0000000..77dcd07
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+
+/**
+ * Interface for interaction between the AccessibilityManagerService
+ * and the ViewRoot in a given window.
+ *
+ * @hide
+ */
+oneway interface IAccessibilityInteractionConnection {
+
+ void findAccessibilityNodeInfoByAccessibilityId(int accessibilityViewId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback);
+
+ void findAccessibilityNodeInfoByViewId(int id, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback);
+
+ void findAccessibilityNodeInfosByViewText(String text, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback);
+
+ void performAccessibilityAction(int accessibilityId, int action, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback);
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
new file mode 100644
index 0000000..9c5e8dc
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+import java.util.List;
+
+/**
+ * Callback for specifying the result for an asynchronous request made
+ * via calling a method on IAccessibilityInteractionConnectionCallback.
+ *
+ * @hide
+ */
+oneway interface IAccessibilityInteractionConnectionCallback {
+
+ void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId);
+
+ void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos,
+ int interactionId);
+
+ void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 6b2aae2..b14f02a 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -18,8 +18,13 @@
package android.view.accessibility;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.IEventListener;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.IWindow;
/**
* Interface implemented by the AccessibilityManagerService called by
@@ -38,4 +43,11 @@
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType);
void interrupt();
+
+ int addAccessibilityInteractionConnection(IWindow windowToken,
+ in IAccessibilityInteractionConnection connection);
+
+ void removeAccessibilityInteractionConnection(IWindow windowToken);
+
+ IAccessibilityServiceConnection registerEventListener(IEventListener client);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9e61ecf..4aad022 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -6461,7 +6461,7 @@
if (hscroll != 0 || vscroll != 0) {
final int vdelta = (int) (vscroll * getVerticalScrollFactor());
final int hdelta = (int) (hscroll * getHorizontalScrollFactor());
- if (pinScrollBy(hdelta, vdelta, true, 0)) {
+ if (pinScrollBy(hdelta, vdelta, false, 0)) {
return true;
}
}
@@ -6503,6 +6503,8 @@
// arrow key events, not trackball events, from one child to the next
private boolean mMapTrackballToArrowKeys = true;
+ private DrawData mDelaySetPicture;
+
public void setMapTrackballToArrowKeys(boolean setMap) {
checkThread();
mMapTrackballToArrowKeys = setMap;
@@ -7912,7 +7914,8 @@
// after WebView's destroy() is called, skip handling messages.
return;
}
- if (mBlockWebkitViewMessages) {
+ if (mBlockWebkitViewMessages
+ && msg.what != WEBCORE_INITIALIZED_MSG_ID) {
// Blocking messages from webkit
return;
}
@@ -8060,6 +8063,10 @@
BrowserFrame.DRAWABLEDIR, mContext);
AssetManager am = mContext.getAssets();
nativeCreate(msg.arg1, drawableDir, am);
+ if (mDelaySetPicture != null) {
+ setNewPicture(mDelaySetPicture);
+ mDelaySetPicture = null;
+ }
break;
case UPDATE_TEXTFIELD_TEXT_MSG_ID:
// Make sure that the textfield is currently focused
@@ -8371,6 +8378,15 @@
}
void setNewPicture(final WebViewCore.DrawData draw) {
+ if (mNativeClass == 0) {
+ if (mDelaySetPicture != null) {
+ throw new IllegalStateException(
+ "Tried to setNewPicture with a delay picture already set! (memory leak)");
+ }
+ // Not initialized yet, delay set
+ mDelaySetPicture = draw;
+ return;
+ }
WebViewCore.ViewState viewState = draw.mViewState;
boolean isPictureAfterFirstLayout = viewState != null;
setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 4ee16e7..6b498fe 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -115,7 +115,7 @@
}
/**
- * Describes how the foreground is positioned. Defaults to BEFORE and TOP.
+ * Describes how the foreground is positioned. Defaults to START and TOP.
*
* @param foregroundGravity See {@link android.view.Gravity}
*
@@ -125,7 +125,7 @@
public void setForegroundGravity(int foregroundGravity) {
if (mForegroundGravity != foregroundGravity) {
if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
- foregroundGravity |= Gravity.BEFORE;
+ foregroundGravity |= Gravity.START;
}
if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index f843574..0cdbc5b 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -116,10 +116,10 @@
equals = Gravity.LEFT, name = "LEFT"),
@ViewDebug.FlagToString(mask = Gravity.RIGHT,
equals = Gravity.RIGHT, name = "RIGHT"),
- @ViewDebug.FlagToString(mask = Gravity.BEFORE,
- equals = Gravity.BEFORE, name = "BEFORE"),
- @ViewDebug.FlagToString(mask = Gravity.AFTER,
- equals = Gravity.AFTER, name = "AFTER"),
+ @ViewDebug.FlagToString(mask = Gravity.START,
+ equals = Gravity.START, name = "START"),
+ @ViewDebug.FlagToString(mask = Gravity.END,
+ equals = Gravity.END, name = "END"),
@ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL,
equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"),
@ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL,
@@ -132,10 +132,10 @@
equals = Gravity.CENTER, name = "CENTER"),
@ViewDebug.FlagToString(mask = Gravity.FILL,
equals = Gravity.FILL, name = "FILL"),
- @ViewDebug.FlagToString(mask = Gravity.RELATIVE_HORIZONTAL_DIRECTION,
- equals = Gravity.RELATIVE_HORIZONTAL_DIRECTION, name = "RELATIVE")
+ @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
+ equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
})
- private int mGravity = Gravity.BEFORE | Gravity.TOP;
+ private int mGravity = Gravity.START | Gravity.TOP;
@ViewDebug.ExportedProperty(category = "measurement")
private int mTotalLength;
@@ -1649,7 +1649,7 @@
public void setGravity(int gravity) {
if (mGravity != gravity) {
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
- gravity |= Gravity.BEFORE;
+ gravity |= Gravity.START;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
@@ -1742,8 +1742,8 @@
@ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"),
@ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"),
@ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"),
- @ViewDebug.IntToString(from = Gravity.BEFORE, to = "BEFORE"),
- @ViewDebug.IntToString(from = Gravity.AFTER, to = "AFTER"),
+ @ViewDebug.IntToString(from = Gravity.START, to = "START"),
+ @ViewDebug.IntToString(from = Gravity.END, to = "END"),
@ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"),
@ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"),
@ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index acd8539..a4771d5 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -16,6 +16,15 @@
package android.widget;
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -32,14 +41,6 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.RemoteViews.RemoteView;
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.SortedSet;
-import java.util.TreeSet;
import static android.util.Log.d;
@@ -222,7 +223,7 @@
public void setGravity(int gravity) {
if (mGravity != gravity) {
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
- gravity |= Gravity.BEFORE;
+ gravity |= Gravity.START;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
@@ -1440,6 +1441,7 @@
);
private Node mNext;
+ private boolean mIsPooled;
public void setNextPoolable(Node element) {
mNext = element;
@@ -1449,6 +1451,14 @@
return mNext;
}
+ public boolean isPooled() {
+ return mIsPooled;
+ }
+
+ public void setPooled(boolean isPooled) {
+ mIsPooled = isPooled;
+ }
+
static Node acquire(View view) {
final Node node = sPool.acquire();
node.view = view;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 35e78fb..eba9d37 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -113,6 +113,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
@@ -2094,7 +2095,7 @@
*/
public void setGravity(int gravity) {
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
- gravity |= Gravity.BEFORE;
+ gravity |= Gravity.START;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.TOP;
@@ -7647,7 +7648,18 @@
protected int computeVerticalScrollExtent() {
return getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
}
-
+
+ @Override
+ public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+ CharSequence thisText = getText();
+ if (TextUtils.isEmpty(thisText)) {
+ return;
+ }
+ if (thisText.toString().toLowerCase().contains(text)) {
+ outViews.add(this);
+ }
+ }
+
public enum BufferType {
NORMAL, SPANNABLE, EDITABLE,
}
@@ -7899,6 +7911,17 @@
event.setPassword(isPassword);
}
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+
+ final boolean isPassword = hasPasswordTransformationMethod();
+ if (!isPassword) {
+ info.setText(getText());
+ }
+ info.setPassword(isPassword);
+ }
+
void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
int fromIndex, int removedCount, int addedCount) {
AccessibilityEvent event =
@@ -10005,7 +10028,7 @@
private boolean mSelectAllOnFocus = false;
- private int mGravity = Gravity.TOP | Gravity.BEFORE;
+ private int mGravity = Gravity.TOP | Gravity.START;
private boolean mHorizontallyScrolling;
private int mAutoLinkMask;
diff --git a/core/java/com/android/internal/util/Objects.java b/core/java/com/android/internal/util/Objects.java
index 598a079..2664182 100644
--- a/core/java/com/android/internal/util/Objects.java
+++ b/core/java/com/android/internal/util/Objects.java
@@ -16,34 +16,47 @@
package com.android.internal.util;
+import java.util.Arrays;
+
/**
* Object utility methods.
*/
public class Objects {
/**
- * Ensures the given object isn't {@code null}.
+ * Determines whether two possibly-null objects are equal. Returns:
*
- * @return the given object
- * @throws NullPointerException if the object is null
+ * <ul>
+ * <li>{@code true} if {@code a} and {@code b} are both null.
+ * <li>{@code true} if {@code a} and {@code b} are both non-null and they are
+ * equal according to {@link Object#equals(Object)}.
+ * <li>{@code false} in all other situations.
+ * </ul>
+ *
+ * <p>This assumes that any non-null objects passed to this function conform
+ * to the {@code equals()} contract.
*/
- public static <T> T nonNull(T t) {
- if (t == null) {
- throw new NullPointerException();
- }
- return t;
+ public static boolean equal(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
}
/**
- * Ensures the given object isn't {@code null}.
+ * Generates a hash code for multiple values. The hash code is generated by
+ * calling {@link Arrays#hashCode(Object[])}.
*
- * @return the given object
- * @throws NullPointerException if the object is null
+ * <p>This is useful for implementing {@link Object#hashCode()}. For example,
+ * in an object that has three properties, {@code x}, {@code y}, and
+ * {@code z}, one could write:
+ * <pre>
+ * public int hashCode() {
+ * return Objects.hashCode(getX(), getY(), getZ());
+ * }</pre>
+ *
+ * <b>Warning</b>: When a single object is supplied, the returned hash code
+ * does not equal the hash code of that object.
*/
- public static <T> T nonNull(T t, String message) {
- if (t == null) {
- throw new NullPointerException(message);
- }
- return t;
+ public static int hashCode(Object... objects) {
+ return Arrays.hashCode(objects);
}
+
}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
new file mode 100644
index 0000000..a53a9c0
--- /dev/null
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+/**
+ * Simple static methods to be called at the start of your own methods to verify
+ * correct arguments and state.
+ */
+public class Preconditions {
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling
+ * method is not null.
+ *
+ * @param reference an object reference
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNull(T reference) {
+ if (reference == null) {
+ throw new NullPointerException();
+ }
+ return reference;
+ }
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling
+ * method is not null.
+ *
+ * @param reference an object reference
+ * @param errorMessage the exception message to use if the check fails; will
+ * be converted to a string using {@link String#valueOf(Object)}
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNull(T reference, Object errorMessage) {
+ if (reference == null) {
+ throw new NullPointerException(String.valueOf(errorMessage));
+ }
+ return reference;
+ }
+
+}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index c41b2cb..b9948fe 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,8 +16,6 @@
package com.android.internal.view;
-import android.content.ClipData;
-import android.content.ClipDescription;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
@@ -26,8 +24,6 @@
import android.view.DragEvent;
import android.view.IWindow;
import android.view.IWindowSession;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
public class BaseIWindow extends IWindow.Stub {
private IWindowSession mSession;
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 4a09232..942aa8a1 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -755,32 +755,46 @@
jfloat x, jfloat y, int flags, SkPaint* paint) {
jint count = end - start;
- sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
- paint, textArray, start, count, end, flags);
+ sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+ value = gTextLayoutCache.getValue(paint, textArray, start, count, end, flags);
if (value == NULL) {
LOGE("Cannot get TextLayoutCache value");
return ;
}
+#else
+ value = new TextLayoutCacheValue();
+ value->computeValues(paint, textArray, start, count, end, flags);
+#endif
+
#if DEBUG_GLYPHS
logGlyphs(value);
#endif
- doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
- x, y, flags, paint);
+
+ doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
+ x, y, flags, paint);
}
static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
int start, int count, int contextCount,
jfloat x, jfloat y, int flags, SkPaint* paint) {
- sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
- paint, textArray, start, count, contextCount, flags);
+ sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+ value = gTextLayoutCache.getValue(paint, textArray, start, count, contextCount, flags);
if (value == NULL) {
LOGE("Cannot get TextLayoutCache value");
return ;
}
+#else
+ value = new TextLayoutCacheValue();
+ value->computeValues(paint, textArray, start, count, contextCount, flags);
+#endif
+
#if DEBUG_GLYPHS
logGlyphs(value);
#endif
+
doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
x, y, flags, paint);
}
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index 46e6c2b..7e89a37 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -254,21 +254,21 @@
void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
jint count, jint contextCount, jint dirFlags,
jfloat* resultAdvances, jfloat& resultTotalAdvance) {
+ sp<TextLayoutCacheValue> value;
#if USE_TEXT_LAYOUT_CACHE
// Return advances from the cache. Compute them if needed
- sp<TextLayoutCacheValue> layout = gTextLayoutCache.getValue(
+ value = gTextLayoutCache.getValue(
paint, chars, start, count, contextCount, dirFlags);
- if (layout != NULL) {
- if (resultAdvances != NULL) {
- memcpy(resultAdvances, layout->getAdvances(), layout->getAdvancesCount() * sizeof(jfloat));
- }
- resultTotalAdvance = layout->getTotalAdvance();
- }
#else
- // Compute advances and return them
- TextLayoutCacheValue::computeValuesWithHarfbuzz(paint, chars, start, count, contextCount,
- dirFlags, resultAdvances, &resultTotalAdvance, NULL, NULL );
+ value = new TextLayoutCacheValue();
+ value->computeValues(paint, chars, start, count, contextCount, dirFlags);
#endif
+ if (value != NULL) {
+ if (resultAdvances != NULL) {
+ memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat));
+ }
+ resultTotalAdvance = value->getTotalAdvance();
+ }
}
void TextLayout::getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start,
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
index ecfe5ff..b36fa3e 100644
--- a/core/jni/android_app_backup_FullBackup.cpp
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -73,6 +73,8 @@
static int backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
jstring domainObj, jstring linkdomain,
jstring rootpathObj, jstring pathObj, jobject dataOutputObj) {
+ int ret;
+
// Extract the various strings, allowing for null object pointers
const char* packagenamechars = env->GetStringUTFChars(packageNameObj, NULL);
const char* rootchars = env->GetStringUTFChars(rootpathObj, NULL);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index e5c28489..0960b25 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -38,18 +38,7 @@
#include <grp.h>
#include <pwd.h>
#include <signal.h>
-
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
+#include <unistd.h>
#define POLICY_DEBUG 0
#define GUARD_THREAD_PRIORITY 0
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 31988f7..57a97bd 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -438,12 +438,17 @@
static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
#if RTL_USE_HARFBUZZ
- sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
- paint, text, 0, count, count, flags);
+ sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+ value = gTextLayoutCache.getValue(paint, text, 0, count, count, flags);
if (value == NULL) {
LOGE("Cannot get TextLayoutCache value");
return ;
}
+#else
+ value = new TextLayoutCacheValue();
+ value->computeValues(paint, text, 0, count, count, flags);
+#endif
#if DEBUG_GLYPHS
logGlyphs(value);
#endif
@@ -466,12 +471,17 @@
jint start, jint count, jint contextCount, jfloat x, jfloat y,
int flags, SkPaint* paint) {
#if RTL_USE_HARFBUZZ
- sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
- paint, text, start, count, contextCount, flags);
+ sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+ value = gTextLayoutCache.getValue(paint, text, start, count, contextCount, flags);
if (value == NULL) {
LOGE("Cannot get TextLayoutCache value");
return ;
}
+#else
+ value = new TextLayoutCacheValue();
+ value->computeValues(paint, text, start, count, contextCount, flags);
+#endif
#if DEBUG_GLYPHS
logGlyphs(value);
#endif
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 110268e..3efb83c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -677,6 +677,14 @@
android:label="@string/permlab_dump"
android:description="@string/permdesc_dump" />
+ <!-- Allows an application to retrieve the content of the active window
+ An active window is the window that has fired an accessibility event. -->
+ <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
+ android:permissionGroup="android.permission-group.PERSONAL_INFO"
+ android:protectionLevel="signatureOrSystem"
+ android:label="@string/permlab_retrieve_window_content"
+ android:description="@string/permdesc_retrieve_window_content" />
+
<!-- Allows an application to open windows using the type
{@link android.view.WindowManager.LayoutParams#TYPE_SYSTEM_ALERT},
shown on top of all other applications. Very few applications
diff --git a/core/res/res/drawable-hdpi/pointer_arrow.png b/core/res/res/drawable-hdpi/pointer_arrow.png
new file mode 100644
index 0000000..fbd187c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_arrow_icon.xml b/core/res/res/drawable-hdpi/pointer_arrow_icon.xml
new file mode 100644
index 0000000..2f5676f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_arrow_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_arrow"
+ android:hotSpotX="6"
+ android:hotSpotY="6" />
diff --git a/core/res/res/drawable-hdpi/pointer_spot_anchor.png b/core/res/res/drawable-hdpi/pointer_spot_anchor.png
new file mode 100644
index 0000000..d7aca36
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_anchor.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml
new file mode 100644
index 0000000..2222b8e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_spot_anchor"
+ android:hotSpotX="33"
+ android:hotSpotY="33" />
diff --git a/core/res/res/drawable-hdpi/pointer_spot_hover.png b/core/res/res/drawable-hdpi/pointer_spot_hover.png
new file mode 100644
index 0000000..5041aa3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_hover.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml
new file mode 100644
index 0000000..dc62a69
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_spot_hover"
+ android:hotSpotX="33"
+ android:hotSpotY="33" />
diff --git a/core/res/res/drawable-hdpi/pointer_spot_touch.png b/core/res/res/drawable-hdpi/pointer_spot_touch.png
new file mode 100644
index 0000000..64a42a1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_touch.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml
new file mode 100644
index 0000000..4bffee6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+ android:bitmap="@drawable/pointer_spot_touch"
+ android:hotSpotX="24"
+ android:hotSpotY="24" />
diff --git a/core/res/res/layout/volume_adjust.xml b/core/res/res/layout/volume_adjust.xml
index b0ca3e8..7303003 100644
--- a/core/res/res/layout/volume_adjust.xml
+++ b/core/res/res/layout/volume_adjust.xml
@@ -15,22 +15,21 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="left">
-
+ android:layout_width="480dp"
+ android:layout_height="wrap_content">
<LinearLayout
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="80dip"
+ android:layout_marginTop="80dp"
android:background="@android:drawable/dialog_full_holo_dark"
android:orientation="horizontal"
>
<LinearLayout
android:id="@+id/slider_group"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_weight="1"
android:orientation="vertical"
>
<!-- Sliders go here -->
@@ -56,9 +55,6 @@
android:background="?attr/selectableItemBackground"
android:src="@drawable/ic_sysbar_quicksettings"
/>
-
+
</LinearLayout>
-
-</FrameLayout>
-
-
+</FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/volume_adjust_item.xml b/core/res/res/layout/volume_adjust_item.xml
index e841d87..beb511d 100644
--- a/core/res/res/layout/volume_adjust_item.xml
+++ b/core/res/res/layout/volume_adjust_item.xml
@@ -15,7 +15,7 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="80dip"
android:orientation="horizontal"
android:layout_marginTop="8dip"
@@ -34,8 +34,9 @@
<SeekBar
style="?android:attr/seekBarStyle"
android:id="@+id/seekbar"
- android:layout_width="300dip"
+ android:layout_width="300dp"
android:layout_height="wrap_content"
+ android:layout_weight="1"
android:padding="16dip"
android:layout_marginLeft="8dip"
android:layout_marginRight="8dip" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 38b62d4..cab43ac 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -412,6 +412,10 @@
<!-- no translation found for permlab_setOrientation (3365947717163866844) -->
<skip />
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Laat \'n program toe om die skerm te enige tyd te draai. Behoort vir gewone programme nooit nodig te wees nie."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
<skip />
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Laat program toe om te versoek dat die voorsiende materiaal aan alle aanhoudende prosesse gestuur word."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9c22f0e..b97bc7a 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -396,6 +396,10 @@
<!-- no translation found for permlab_setOrientation (3365947717163866844) -->
<skip />
<string name="permdesc_setOrientation" msgid="6335814461615851863">"ትግበራው በማንኛውም ሰዓት የማያንማሽከርከሪያለመለወጥ ይፈቅዳል።ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
<skip />
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"ትግበራዎች ለሁሉም ተከታታይ ሂደቶች ልከው የሚያቀርቧቸው ሲግናሎችን ለመጠየቅ ይፈቅዳል።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index c9454d3..8d5ecb8 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"للسماح للمالك بإرسال الأهداف إلى أحد مشرفي الجهاز. لا يجب استخدامه على الإطلاق للتطبيقات العادية."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"تغيير اتجاه الشاشة"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"للسماح لتطبيق ما بتغيير تدوير الشاشة في أي وقت. لا يجب استخدامه على الإطلاق للتطبيقات العادية."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"إرسال إشارات Linux للتطبيقات"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"للسماح للتطبيق بطلب إرسال الإشارة المزوّدة لجميع العمليات المستمرة."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"تشغيل التطبيق دائمًا"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 70eb79a..b43b50f 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Разрешава на притежателя да изпраща намерения до администратор на устройството. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"промяна на ориентацията на екрана"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Разрешава на приложението да променя ориентацията на екрана по всяко време. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"изпращане на сигнали от Linux до приложенията"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Разрешава на приложението да подаде заявка предоставеният сигнал да се изпрати до всички постоянни процеси."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"задаване на постоянно изпълнение на приложението"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 45201c1..2947b93 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permet al titular enviar intencions a un administrador del sistema. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"canviar l\'orientació de la pantalla"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permet a una aplicació canviar el gir de la pantalla en qualsevol moment. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar senyals Linux a les aplicacions"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permet a l\'aplicació sol·licitar que el senyal subministrat s\'enviï a tots els processos persistents."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"fer que l\'aplicació s\'executi sempre"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f88e381..92deaca 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Umožňuje držiteli oprávnění odesílat informace správci zařízení. Běžné aplikace by toto oprávnění nikdy neměly požadovat."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"změna orientace obrazovky"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Umožňuje aplikaci kdykoli změnit orientaci obrazovky. Běžné aplikace by toto nastavení nikdy neměly využívat."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"odeslání signálů Linux aplikacím"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Umožňuje aplikaci vyžádat zaslání poskytnutého signálu všem trvalým procesům."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"trvalé spuštění aplikace"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index c6946d4..a3149a2 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillader brugeren at sende hensigter til en enhedsadministrator. Bør aldrig være nødvendigt for almindelige programmer."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"skift skærmretning"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillader, at et program ændrer rotationen af skærmen når som helst. Bør aldrig være nødvendigt til normale programmer."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"send Linux-signaler til programmer"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Tillader, at programmet kan anmode om, at det leverede signal sendes til alle vedholdende processer."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"lad altid programmet køre"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ffef226..b6b6be5 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Ermöglicht dem Halter, Intents an einen Geräteadministrator zu senden. Sollte nie für normale Anwendungen benötigt werden."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"Bildschirmausrichtung ändern"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Ermöglicht der Anwendung, die Bildschirmdrehung jederzeit zu ändern. Sollte nicht für normale Anwendungen benötigt werden."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-Signale an Anwendungen senden"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ermöglicht der Anwendung, das Senden des gelieferten Signals an alle anhaltenden Prozesse zu fordern"</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"Anwendungen permanent ausführen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index bf1d432f..a5e13f1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Επιτρέπει στον κάτοχο την αποστολή στόχων σε έναν διαχειριστή συσκευής. Δεν θα χρειαστεί ποτέ για κανονικές εφαρμογές."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"αλλαγή προσανατολισμού οθόνης"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Επιτρέπει σε μια εφαρμογή την αλλαγή της περιστροφής της οθόνης οποιαδήποτε στιγμή. Δεν είναι απαραίτητο για κανονικές εφαρμογές."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"αποστολή σημάτων Linux σε εφαρμογές"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Επιτρέπει σε μια εφαρμογή την αποστολή αιτήματος για την αποστολή του παρεχόμενου σήματος σε όλες τις υπάρχουσες διαδικασίες."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"η εφαρμογή να εκτελείται συνεχώς"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index b0e77ba..d5ef98b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Allows the holder to send intents to a device administrator. Should never be needed for normal applications."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"change screen orientation"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Allows an application to change the rotation of the screen at any time. Should never be needed for normal applications."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"send Linux signals to applications"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Allows application to request that the supplied signal be sent to all persistent processes."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"make application always run"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index fb97c4a..3cb61e4 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite que el propietario envíe sus intentos a un administrador de dispositivos. No se necesita para las aplicaciones normales."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar la orientación de la pantalla"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Admite una aplicación que cambia la rotación de la pantalla en cualquier momento. Se debe evitar utilizarlo en aplicaciones normales."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales de Linux a las aplicaciones"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Admite que la aplicación solicite el envío de la señal suministrada a todos los procesos continuos."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"hacer que siempre se ejecute la aplicación"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index a1e3a54..9db80ca 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite enviar intentos a un administrador de dispositivos. Este permiso nunca debería ser necesario para las aplicaciones normales."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar orientación de la pantalla"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que una aplicación cambie la rotación de la pantalla en cualquier momento. No debería ser necesario nunca para las aplicaciones normales."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales Linux a aplicaciones"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite que la aplicación solicite que la señal suministrada se envíe a todos los procesos persistentes."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"hacer que la aplicación se ejecute siempre"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 00d173d..3fcb21d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"به نگهدارنده اجازه می دهد مفاد را به یک سرپرست دستگاه ارسال کند. هرگز برای برنامه های معمولی مورد نیاز نیست."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"تغییر جهت صفحه"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"به یک برنامه کاربردی اجازه می دهد چرخش صفحه را در هر زمانی تغییر دهد. هرگز برای برنامه های معمولی مورد نیاز نیست."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"ارسال سیگنال های Linux برای برنامه ها"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"به برنامه کاربردی اجازه می دهد درخواست کند که سیگنال ارائه شده را به تمام فرایندهای ثابت ارسال کند."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"برنامه همیشه اجرا شود"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 2d3bf48..70e5ff7 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Antaa sovelluksen lähettää kyselyitä laitteen järjestelmänvalvojalle. Ei tavallisten sovelluksien käyttöön."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"muuta näytön suuntaa"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Antaa sovelluksen vaihtaa näytön suuntaa milloin tahansa. Ei tavallisten sovelluksien käyttöön."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"lähettää sovelluksiin Linux-signaaleja"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Antaa sovelluksen pyytää, että signaali lähetetään kaikille kiinteille prosesseille."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"aseta sovellus olemaan jatkuvasti käynnissä"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 3f3eb93..c618edf 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permet à l\'application d\'envoyer des intentions à l\'administrateur de l\'appareil. Les applications standard ne devraient jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"Changement d\'orientation de l\'écran"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permet à une application de modifier la rotation de l\'écran à tout moment. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Envoi de signaux Linux aux applications"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permet à une application de demander que le signal fourni soit envoyé à tous les processus persistants."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"Exécution de l\'application en continu"</string>
@@ -449,7 +453,7 @@
<string name="permlab_nfc" msgid="4423351274757876953">"contrôler la communication en champ proche"</string>
<string name="permdesc_nfc" msgid="9171401851954407226">"Permet à une application de communiquer avec des tags, cartes et lecteurs prenant en charge la communication en champ proche (NFC)."</string>
<string name="permlab_vpn" msgid="8345800584532175312">"intercepter et modifier l\'ensemble du trafic réseau"</string>
- <string name="permdesc_vpn" msgid="5617893078989944219">"Permet à une application d\'intercepter et d\'inspecter l\'ensemble du trafic réseau, par exemple pour établir une connexion VPN. Des applications malveillantes sont susceptibles de surveiller, rediriger ou modifier les paquets réseau à votre insu."</string>
+ <string name="permdesc_vpn" msgid="5617893078989944219">"Permet à une application d\'intercepter et de contrôler l\'ensemble du trafic réseau, par exemple pour établir une connexion VPN. Des applications malveillantes sont susceptibles de surveiller, rediriger ou modifier les paquets réseau à votre insu."</string>
<string name="permlab_disableKeyguard" msgid="4977406164311535092">"Désactivation du verrouillage des touches"</string>
<string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permet à une application de désactiver le verrouillage des touches et toute sécurité par mot de passe. Exemple : Votre téléphone désactive le verrouillage du clavier lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"Lecture des paramètres de synchronisation"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 773a8f3..f17a272 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Nositelju omogućuje slanje namjera administratoru uređaja. Nikad ne bi trebalo koristiti za uobičajene aplikacije."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"promjena orijentacije zaslona"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Aplikaciji omogućuje promjenu rotacije zaslona u svakom trenutku. Nikad ne bi trebalo koristiti za uobičajene aplikacije."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"slanje Linux signala u aplikaciju"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Aplikacijama omogućuje traženje zahtjeva da se dobiveni signal pošalje na sve trajne postupke."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"uvijek pokrenuta aplikacija"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 09dd648..0c2aef3 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Lehetővé teszi a használó számára, hogy célokat küldjön egy eszközkezelőnek. A normál alkalmazásoknak erre soha nincs szüksége."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"képernyő irányának módosítása"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Lehetővé teszi egy alkalmazás számára a képernyő elforgatásának módosítását. A normál alkalmazásoknak erre soha nincs szüksége."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux jelek küldése alkalmazásoknak"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Lehetővé teszi az alkalmazás számára, hogy a megadott jelet elküldje az összes állandó folyamatnak."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"alkalmazások folyamatos futtatása"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b2f3af8..00e2dd4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Mengizinkan pemegang mengirimkan tujuan kepada administrator perangkat. Tidak diperlukan untuk aplikasi normal."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"ubah orientasi layar"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Mengizinkan aplikasi mengubah rotasi layar kapan saja. Tidak diperlukan untuk aplikasi normal."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"kirim sinyal Linux ke aplikasi"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Mengizinkan aplikasi meminta sinyal yang diberikan untuk dikirimkan ke semua proses yang ada."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"jadikan aplikasi selalu berjalan"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 19d317e..0d78861 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Consente l\'invio di intent a un amministratore del dispositivo. L\'autorizzazione non deve mai essere necessaria per le normali applicazioni."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"modifica orientamento dello schermo"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Consente a un\'applicazione di cambiare la rotazione dello schermo in qualsiasi momento. Non dovrebbe essere mai necessario per le normali applicazioni."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"invio segnali Linuz alle applicazioni"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Consente all\'applicazione di richiedere l\'invio del segnale fornito a tutti i processi persistenti."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"esecuzione permanente delle applicazioni"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 4aba678..6b50982 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"מאפשר למשתמש לשלוח כוונות למנהל התקן. לא אמור להידרש לעולם ביישומים רגילים."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"שנה את כיוון המסך"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"מאפשר ליישום לשנות את סיבוב המסך בכל עת. לא אמור להידרש לעולם ביישומים רגילים."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"שלח אותות Linux ליישומים"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"מאפשר ליישום לבקש שהאות המסופק יישלח לכל התהליכים המתמשכים."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"הגדר את היישום לפעול תמיד"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e078274..bdad7b6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"デバイス管理者へのintentの送信を所有者に許可します。通常のアプリケーションでは不要です。"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"画面の向きの変更"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"いつでも画面の回転を変更することをアプリケーションに許可します。通常のアプリケーションにはまったく必要ありません。"</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linuxのシグナルをアプリケーションに送信"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"受信した電波を継続プロセスに送信することをアプリケーションに許可します。"</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"アプリケーションを常に実行する"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4d1778e..5a3584a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"보유자가 기기 관리자에게 인텐트를 보낼 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"화면 방향 변경"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"애플리케이션이 언제든지 화면 회전을 변경할 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"애플리케이션에 Linux 시그널 보내기"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"애플리케이션이 제공된 시그널을 모든 영구 프로세스로 보내도록 요청할 수 있도록 합니다."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"애플리케이션이 항상 실행되도록 설정"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 5b7ef8c..eab92c6 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Leidžia savininkui siųsti tikslus įrenginio administratoriui. Neturėtų reikėti įprastose programose."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"keisti ekrano padėtį"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Leidžia programai bet kuriuo metu keisti ekrano sukimą. Neturėtų reikėti įprastoms programoms."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"programoms siųsti „Linux“ signalus"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Leidžia programai pateikti užklausą, kad teikiamas signalas būtų siunčiamas visiems nuolatiniams procesams."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"nustatyti, kad programa būtų visada paleista"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 2aa49ca..5c5525c 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Ļauj īpašniekam sūtīt nolūkus ierīces administratoram. Nekad nav nepieciešams parastajām lietojumprogrammām."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"mainīt ekrāna orientāciju"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Ļauj lietojumprogrammai jebkurā brīdī mainīt ekrāna pagriešanas iestatījumu. Parastajām lietojumprogrammām nekad nav nepieciešama."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"sūtīt Linux signālus uz lietojumprogrammām"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ļauj lietojumprogrammai pieprasīt, lai piegādātais signāls tiktu sūtīts uz visiem pastāvīgajiem procesiem."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"likt lietojumprogrammai vienmēr darboties"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index e82fc10..6b0e18e 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -297,6 +297,10 @@
<skip />
<string name="permlab_setOrientation" msgid="3365947717163866844">"tukar orientasi skrin"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Membolehkan aplikasi untuk menukar putaran skrin pada bila-bila masa. Tidak seharusnya diperlukan untuk aplikasi biasa."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"hantar isyarat Linux kepada aplikasi"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Membenarkan aplikasi meminta isyarat yang dibekalkan dihantar kepada semua proses gigih."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"buatkan aplikasi sentiasa berjalan"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 02a414c..2413685 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillater innehaveren å sende hensikter til enhetsadministrator. Bør aldri være nødvendig for normale programmer."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"snu skjermen"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Lar applikasjonen rotere skjermen når som helst. Vanlige applikasjoner bør aldri trenge dette."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"sende Linux-signaler til applikasjoner"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Lar applikasjonen spørre om at et gitt signal blir sendt til alle varige prosesser."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"forbli kjørende"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index ad214bf..dbb7e98 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Staat de houder toe intenties te verzenden naar een apparaatbeheerder. Nooit vereist voor normale toepassingen."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"schermstand wijzigen"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Hiermee kan een app op elk gewenst moment de oriëntatie van het scherm wijzigen. Nooit vereist voor normale apps."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-signalen verzenden naar toepassingen"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Hiermee kan de app ervoor zorgen dat het geleverde signaal wordt verzonden naar alle persistente processen."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"app altijd laten uitvoeren"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ed877bb..36d3cdd 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Zezwala posiadaczowi na wysyłanie informacji o zamiarach do administratora urządzenia. Opcja nie powinna być nigdy potrzebna w przypadku zwykłych aplikacji."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"zmienianie orientacji ekranu"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Pozwala aplikacji na zmianę orientacji ekranu w dowolnym momencie. Nigdy nie powinno być potrzeby stosowania w normalnych aplikacjach."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"wysyłanie sygnałów systemu Linux do aplikacji"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Pozwala aplikacjom żądać, aby dostarczany sygnał był wysyłany do wszystkich trwających procesów."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"sprawianie, że aplikacja jest cały czas uruchomiona"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 7ba9ad0..9cec4b6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite ao titular enviar intenções para um administrador do dispositivo. Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"mudar orientação do ecrã"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite a uma aplicação mudar a rotação do ecrã em qualquer momento. Nunca deve ser necessário para aplicações normais."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais Linux para aplicações"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite à aplicação pedir que o sinal fornecido seja enviado a todos os processos persistentes."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"fazer com que a aplicação seja sempre executada"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 750a751..57737c9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite que o detentor envie tentativas ao administrador de um dispositivo. Não é necessário para aplicativos normais."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"alterar orientação da tela"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que um aplicativo altere a rotação da tela a qualquer momento. Aplicativos normais não devem precisar disso em momento algum."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais de Linux para os aplicativos"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite que o aplicativo solicite que o sinal fornecido seja enviado a todos os processos persistentes."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"executar sempre o aplicativo"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index ff2eecb..60f6049 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -275,6 +275,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permetta al possessur da trametter intenziuns a l\'administratur dal apparat periferic. Betg previs per applicaziuns normalas."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"midar l\'orientaziun dal visur"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permetta a l\'applicaziun da midar da tut temp la orientaziun dal visur. Betg previs per applicaziuns normalas."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"trametter signals Linux a las applicaziuns"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permetta a l\'applicaziun da pretender ch\'il signal furnì vegnia tramess a tut ils process persistents."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"exequir permanentamain applicaziuns"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 7fb4f73..1961903 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite proprietarului să trimită intenţii către un administrator al dispozitivului. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"modificare orientare ecran"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite unei aplicaţii să modifice rotaţia ecranului în orice moment. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"trimitere semnale Linux către aplicaţii"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite aplicaţiei să solicite trimiterea semnalului furnizat către toate procesele persistente."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"rulare întotdeauna a aplicaţiei"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index fe5815f..83dc30f 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Позволяет владельцу отправлять целевые значения администратору устройства. Никогда не используется обычными приложениями."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"изменять ориентацию экрана"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Позволяет приложению изменять ориентацию экрана в любое время. Не требуется для обычных приложений."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"отправлять приложениям сигналы Linux"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Позволяет приложению направлять запрос на передачу предоставленного сигнала всем постоянным процессам."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"запускать постоянную работу приложения"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index d7d9725..3439f59 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Umožňuje držiteľovi odosielať informácie správcovi zariadenia. Bežné aplikácie by toto oprávnenie nemali nikdy požadovať."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"zmena orientácie obrazovky"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Umožňuje aplikácii kedykoľvek zmeniť orientáciu obrazovky. Bežné aplikácie by toto nastavenie nemali vôbec využívať."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"odoslanie signálov systému Linux aplikáciám"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Umožňuje aplikácii vyžiadať zaslanie poskytnutého signálu všetkým trvalým procesom."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"trvalé spustenie aplikácie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index cbb097b..cd4cd07 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Dovoljuje lastniku, da pošlje namere skrbniku naprave. Nikoli se ne uporablja za navadne programe."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"spreminjanje usmerjenosti zaslona"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Dovoljuje, da program kadar koli spremeni smer sukanja zaslona. Tega nikoli ni treba uporabiti za navadne programe."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"pošiljanje signalov Linuxa programom"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Dovoljuje, da program zahteva, da je posredovan signal poslan vsem trajnim procesom."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"neprekinjeno izvajanje programov"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 93faf2f..59329b4 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Омогућава власнику да шаље своје намере администратору уређаја. Обичне апликације никада не би требало да је користе."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"промена положаја екрана"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Омогућава да апликација у сваком тренутку промени ротацију екрана. Обичне апликације никада не би требало да је користе."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"слање Linux сигнала апликацијама"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Омогућава да апликација захтева да испоручени сигнал буде послат свим трајним процесима."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"омогућавање непрекидне активности апликације"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index c3561d7..a3350e5 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillåter att innehavaren skickar avsikter till en enhetsadministratör. Vanliga program behöver aldrig göra detta."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"ändra bildskärmens rikting"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillåter att ett program när som helst ändrar skärmens rotering. Behövs inte för vanliga program."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"skicka Linux-signaler till appar"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Tillåter att programmet begär att den angivna signalen skickas till alla beständiga processer."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"se till att programmet alltid körs"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 453f74e..ff1907a 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -396,6 +396,10 @@
<!-- no translation found for permlab_setOrientation (3365947717163866844) -->
<skip />
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Huruhusu programu kubadilisha uzungukaji wa skrini wakati wowote. Haipaswi kuhitajika kwa programu za kawaida za kompyuta."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
<skip />
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Huruhusu programu kuomba kwamba mawimbi yaliyotolewa yatumwe kwa michakato yote isiyokoma."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 54b33f4..be958dc 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"อนุญาตให้ผู้ถือส่งเนื้อหาไปยังโปรแกรมควบคุมอุปกรณ์ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"เปลี่ยนการวางแนวหน้าจอ"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"อนุญาตให้แอปพลิเคชันเปลี่ยนการหมุนหน้าจอได้ตลอดเวลา ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"ส่งสัญญาณ Linux ไปยังแอปพลิเคชัน"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"อนุญาตให้แอปพลิเคชันร้องขอให้ส่งสัญญาณแจ้งไปยังกระบวนการที่ยังทำงานอยู่ทั้งหมด"</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"ทำให้แอปพลิเคชันทำงานตลอดเวลา"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 9eeac34..b6942a4 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Pinapayagan ang holder na magpadala ng mga intensyon sa administrator ng device. Hindi kailanman dapat kailanganin para sa mga normal na application."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"baguhin ang orientation ng screen"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Pinapayagan ang isang application na baguhin ang pag-rotate ng screen anumang oras. Hindi kailanman dapat kailanganin para sa mga normal na application."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"magpadala ng mga Linux signal sa mga application"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Pinapayagan ang application na hilinging ipadala ang na-supply na signal sa lahat ng mga paulit-ulit na proseso."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"palaging patakbuhin ang mga application"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 7f1d3c3..e97d834 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Cihazın sahibinin cihaz yöneticisine amaç göndermesine izin verir. Normal uygulamalarda hiçbir zaman gerek duyulmamalıdır."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"ekran yönünü değiştir"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Uygulamaların ekran yönünü istedikleri zaman değiştirmesine izin verir. Normal uygulamalarda hiçbir zaman gerekmemelidir."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"uygulamalara Linux sinyalleri gönder"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Uygulamaların, sağlanan sinyalin tüm kalıcı işlemlere gönderilmesini istemesine izin verir."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"uygulamayı her zaman çalıştır"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 00e6333..07b11f7 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Дозволяє власнику надсилати цілі адміністратору пристрою. Ніколи не потрібний для звичайних програм."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"змінювати орієнтацію екрана"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Дозволяє програмі будь-коли змінювати обертання екрана. Ніколи не потрібний для звичайних програм."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"надсилати сигнали Linux програмам"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Дозволяє програмі подавати запит щодо надсилання наданого сигналу всім постійним процесам."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"збер. програму завжди запущ."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 45c3ad8..cfb53ac 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Cho phép chủ nhân gửi các ý định đến quản trị viên thiết bị. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"thay đổi hướng màn hình"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Cho phép ứng dụng thay đổi việc xoay màn hình bất kỳ khi nào. Không cần thiết cho các ứng dụng thông thường."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"gửi tín hiệu Linux đến ứng dụng"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Cho phép ứng dụng yêu cầu tín hiệu đã cung cấp được gửi đến tất cả các quá trình liên tục."</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"đặt ứng dụng luôn chạy"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index f5ee751..b45b346 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"允许持有对象将意向发送到设备管理器。普通的应用程序一律无需此权限。"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"更改屏幕显示方向"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"允许应用程序随时更改屏幕的旋转方向。普通应用程序从不需要使用此权限。"</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"向应用程序发送 Linux 信号"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"允许应用程序请求将所提供的信号发送给所有持久进程。"</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"让应用程序始终运行"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 1779baf..399a5b0 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -265,6 +265,10 @@
<string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"允許應用程式將調用請求 (intent) 傳送至裝置管理員;一般應用程式不需使用此選項。"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"變更螢幕顯示方向"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"允許應用程式可隨時變更螢幕旋轉方向。一般應用不應使用這項功能。"</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"傳送 Linux 訊號到應用程式"</string>
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"允許應用程式要求將支援的訊號傳送到所有持續的程序。"</string>
<string name="permlab_persistentActivity" msgid="8659652042401085862">"設定應用程式持續執行"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ad143a7..6f147e7 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -396,6 +396,10 @@
<!-- no translation found for permlab_setOrientation (3365947717163866844) -->
<skip />
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Ivumela uhlelo lokusebenza ukushintsha ukujikeleleza kwesikrini nganoma isiphi isikhathi. Ayisoze yadingeka izinhlelo zokusebenza ezivamile."</string>
+ <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+ <skip />
+ <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+ <skip />
<!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
<skip />
<string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ivumela uhlelo lokusebenza ukucela ukuthi isiginali enikeziwe ithunyelwe kuzo zonke izinqubo eziphikelelayo"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9c55627..4a7c690 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1191,9 +1191,9 @@
edge, a right gravity will clip the left edge, and neither will clip both edges. -->
<flag name="clip_horizontal" value="0x08" />
<!-- Push object to the beginning of its container, not changing its size. -->
- <flag name="before" value="0x00800003" />
+ <flag name="start" value="0x00800003" />
<!-- Push object to the end of its container, not changing its size. -->
- <flag name="after" value="0x00800005" />
+ <flag name="end" value="0x00800005" />
</attr>
<!-- Standard orientation constant. -->
@@ -1932,11 +1932,11 @@
</attr>
<!-- Defines the direction of layout drawing. This typically is associated with writing
- direction of the language script used. The possible values are Left-to-Right,
- Right-to-Left, Locale and Inherit from parent view. If there is nothing to inherit,
- Locale is used. Locale fallsback to 'en-US'. Left-to-Right is the direction used in
- 'en-US'. The default for this attribute is 'inherit'. -->
- <attr name="horizontalDirection">
+ direction of the language script used. The possible values are "ltr" for Left-to-Right,
+ "rtl" for Right-to-Left, "locale" and "inherit" from parent view. If there is nothing
+ to inherit, "locale" is used. "locale" falls back to "en-US". "ltr" is the direction
+ used in "en-US". The default for this attribute is "inherit". -->
+ <attr name="layoutDirection">
<!-- Left-to-Right -->
<enum name="ltr" value="0" />
<!-- Right-to-Left -->
@@ -5156,6 +5156,8 @@
<!-- number of megabytes of storage MTP should reserve for free storage
(used for emulated storage that is shared with system's data partition) -->
<attr name="mtpReserve" format="integer" />
+ <!-- true if the storage can be shared via USB mass storage -->
+ <attr name="allowMassStorage" format="boolean" />
</declare-styleable>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc14a2a..f9fa51c 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -156,6 +156,7 @@
<!-- Regex array of allowable upstream ifaces for tethering - for example if you want
tethering on a new interface called "foo2" add <item>"foo\\d"</item> to the array -->
+ <!-- Interfaces will be prioritized according to the order listed -->
<string-array translatable="false" name="config_tether_upstream_regexs">
</string-array>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 81f888d..55312e3 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1657,6 +1657,9 @@
<public type="attr" name="compatibleWidthLimitDp" />
<public type="attr" name="largestWidthLimitDp" />
+ <public type="style" name="Theme.Holo.Light.NoActionBar" />
+ <public type="style" name="Theme.Holo.Light.NoActionBar.Fullscreen" />
+
<!-- ===============================================================
Resources added in version 13 of the platform (Ice Cream Sandwich)
=============================================================== -->
@@ -1666,7 +1669,6 @@
<public type="attr" name="state_drag_hovered" />
<public type="attr" name="stopWithTask" />
- <public type="style" name="Theme.Holo.Light.NoActionBar" />
<public type="style" name="TextAppearance.SuggestionHighlight" />
<public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" />
<public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" />
@@ -1675,7 +1677,8 @@
<public type="attr" name="textEditSuggestionsBottomWindowLayout" />
<public type="attr" name="textEditSuggestionsTopWindowLayout" />
<public type="attr" name="textEditSuggestionItemLayout" />
- <public type="attr" name="horizontalDirection" />
+
+ <public type="attr" name="layoutDirection" />
<public type="attr" name="fullBackupAgent" />
<public type="attr" name="suggestionsEnabled" />
@@ -1703,4 +1706,5 @@
<public type="attr" name="notificationTimeout" />
<public type="attr" name="accessibilityFlags" />
<public type="attr" name="canRetrieveWindowContent" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8a4b74b..29c23a68 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -562,6 +562,13 @@
never normally need.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_retrieve_window_content">retrieve screen content</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_retrieve_window_content">Allows application to retrieve
+ the content of the active window. Malicious applications may retrieve
+ the entire window content and examine all its text except passwords.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_shutdown">partial shutdown</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_shutdown">Puts the activity manager into a shutdown
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index b02d904..00f47fa 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -12,7 +12,7 @@
$(call all-java-files-under, EnabledTestApp/src)
LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCoreTests
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6084dd2..8e2d925 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -98,6 +98,9 @@
<uses-permission android:name="android.permission.ASEC_RENAME" />
<uses-permission android:name="android.permission.SHUTDOWN" />
+ <!-- accessibility test permissions -->
+ <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" />
+
<application android:theme="@style/Theme">
<uses-library android:name="android.test.runner" />
<activity android:name="android.view.ViewAttachTestActivity" android:label="View Attach Test">
@@ -1225,6 +1228,13 @@
</intent-filter>
</activity>
+ <activity android:name="android.accessibilityservice.InterrogationActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/core/tests/coretests/res/layout/interrogation_activity.xml b/core/tests/coretests/res/layout/interrogation_activity.xml
new file mode 100644
index 0000000..28d965b
--- /dev/null
+++ b/core/tests/coretests/res/layout/interrogation_activity.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, 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:id="@+id/root"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ >
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <Button
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button1"
+ />
+ <Button
+ android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button2"
+ />
+ <Button
+ android:id="@+id/button3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button3"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <Button
+ android:id="@+id/button4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button4"
+ />
+ <Button
+ android:id="@+id/button5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button5"
+ />
+ <Button
+ android:id="@+id/button6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button6"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <Button
+ android:id="@+id/button7"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button7"
+ />
+ <Button
+ android:id="@+id/button8"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button8"
+ />
+ <Button
+ android:id="@+id/button9"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/button9"
+ />
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index 807386a..f51b08e 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -117,4 +117,16 @@
<string name="searchable_label">SearchManager Test</string>
<string name="searchable_hint">A search hint</string>
+
+ <!-- InterrogationActivity -->
+ <string name="button1">Button1</string>
+ <string name="button2">Button2</string>
+ <string name="button3">Button3</string>
+ <string name="button4">Button4</string>
+ <string name="button5">Button5</string>
+ <string name="button6">Button6</string>
+ <string name="button7">Button7</string>
+ <string name="button8">Button8</string>
+ <string name="button9">Button9</string>
+
</resources>
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java
new file mode 100644
index 0000000..b4a0581
--- /dev/null
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) 2011 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.accessibilityservice;
+
+import com.android.frameworks.coretests.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * Activity for testing the accessibility APIs for "interrogation" of
+ * the screen content. These APIs allow exploring the screen and
+ * requesting an action to be performed on a given view from an
+ * AccessiiblityService.
+ */
+public class InterrogationActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.interrogation_activity);
+
+ findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ /* do nothing */
+ }
+ });
+ findViewById(R.id.button5).setOnLongClickListener(new View.OnLongClickListener() {
+ public boolean onLongClick(View v) {
+ return true;
+ }
+ });
+ }
+}
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
new file mode 100644
index 0000000..a20cc1f
--- /dev/null
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
@@ -0,0 +1,464 @@
+/**
+ * Copyright (C) 2011 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.accessibilityservice;
+
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SELECT;
+
+import com.android.frameworks.coretests.R;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityManager;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * Activity for testing the accessibility APIs for "interrogation" of
+ * the screen content. These APIs allow exploring the screen and
+ * requesting an action to be performed on a given view from an
+ * AccessiiblityService.
+ */
+public class InterrogationActivityTest
+ extends ActivityInstrumentationTestCase2<InterrogationActivity> {
+
+ // Timeout before give up wait for the system to process an accessibility setting change.
+ private static final int TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING = 2000;
+
+ // Helpers to figure out the first and last test methods
+ // This is a workaround for the lack of such support in JUnit3
+ private static int sTestMethodCount;
+ private static int sExecutedTestMethodCount;
+
+ // Handle to a connection to the AccessibilityManagerService
+ private static IAccessibilityServiceConnection sConnection;
+
+ // The last received accessibility event
+ private static volatile AccessibilityEvent sLastAccessibilityEvent;
+
+ public InterrogationActivityTest() {
+ super(InterrogationActivity.class);
+ sTestMethodCount = getTestMethodCount();
+ }
+
+ @LargeTest
+ public void testFindAccessibilityNodeInfoByViewId() throws Exception {
+ beforeClassIfNeeded();
+ try {
+ // bring up the activity
+ getActivity();
+
+ AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+ R.id.button5);
+ assertNotNull(button);
+ assertEquals(0, button.getChildCount());
+
+ // bounds
+ Rect bounds = new Rect();
+ button.getBounds(bounds);
+ assertEquals(0, bounds.left);
+ assertEquals(0, bounds.top);
+ assertEquals(73, bounds.right);
+ assertEquals(48, bounds.bottom);
+
+ // char sequence attributes
+ assertEquals("com.android.frameworks.coretests", button.getPackageName());
+ assertEquals("android.widget.Button", button.getClassName());
+ assertEquals("Button5", button.getText());
+ assertNull(button.getContentDescription());
+
+ // boolean attributes
+ assertTrue(button.isFocusable());
+ assertTrue(button.isClickable());
+ assertTrue(button.isEnabled());
+ assertFalse(button.isFocused());
+ assertTrue(button.isClickable());
+ assertFalse(button.isPassword());
+ assertFalse(button.isSelected());
+ assertFalse(button.isCheckable());
+ assertFalse(button.isChecked());
+
+ // actions
+ assertEquals(ACTION_FOCUS | ACTION_SELECT | ACTION_CLEAR_SELECTION,
+ button.getActions());
+ } finally {
+ afterClassIfNeeded();
+ }
+ }
+
+ @LargeTest
+ public void testFindAccessibilityNodeInfoByViewText() throws Exception {
+ beforeClassIfNeeded();
+ try {
+ // bring up the activity
+ getActivity();
+
+ // find a view by text
+ List<AccessibilityNodeInfo> buttons =
+ getConnection().findAccessibilityNodeInfosByViewText("butto");
+ assertEquals(9, buttons.size());
+ } finally {
+ afterClassIfNeeded();
+ }
+ }
+
+ @LargeTest
+ public void testTraverseAllViews() throws Exception {
+ beforeClassIfNeeded();
+ try {
+ // bring up the activity
+ getActivity();
+
+ // make list of expected nodes
+ List<String> classNameAndTextList = new ArrayList<String>();
+ classNameAndTextList.add("android.widget.LinearLayout");
+ classNameAndTextList.add("android.widget.LinearLayout");
+ classNameAndTextList.add("android.widget.LinearLayout");
+ classNameAndTextList.add("android.widget.LinearLayout");
+ classNameAndTextList.add("android.widget.ButtonButton1");
+ classNameAndTextList.add("android.widget.ButtonButton2");
+ classNameAndTextList.add("android.widget.ButtonButton3");
+ classNameAndTextList.add("android.widget.ButtonButton4");
+ classNameAndTextList.add("android.widget.ButtonButton5");
+ classNameAndTextList.add("android.widget.ButtonButton6");
+ classNameAndTextList.add("android.widget.ButtonButton7");
+ classNameAndTextList.add("android.widget.ButtonButton8");
+ classNameAndTextList.add("android.widget.ButtonButton9");
+
+ AccessibilityNodeInfo root = getConnection().findAccessibilityNodeInfoByViewId(
+ R.id.root);
+ assertNotNull("We must find the existing root.", root);
+
+ Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
+ fringe.add(root);
+
+ // do a BFS traversal and check nodes
+ while (!fringe.isEmpty()) {
+ AccessibilityNodeInfo current = fringe.poll();
+
+ CharSequence className = current.getClassName();
+ CharSequence text = current.getText();
+ String receivedClassNameAndText = className.toString()
+ + ((text != null) ? text.toString() : "");
+ String expectedClassNameAndText = classNameAndTextList.remove(0);
+
+ assertEquals("Did not get the expected node info",
+ expectedClassNameAndText, receivedClassNameAndText);
+
+ final int childCount = current.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ AccessibilityNodeInfo child = current.getChild(i);
+ fringe.add(child);
+ }
+ }
+ } finally {
+ afterClassIfNeeded();
+ }
+ }
+
+ @LargeTest
+ public void testPerformAccessibilityActionFocus() throws Exception {
+ beforeClassIfNeeded();
+ try {
+ // bring up the activity
+ getActivity();
+
+ // find a view and make sure it is not focused
+ AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+ R.id.button5);
+ assertFalse(button.isFocused());
+
+ // focus the view
+ assertTrue(button.performAction(ACTION_FOCUS));
+
+ // find the view again and make sure it is focused
+ button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+ assertTrue(button.isFocused());
+ } finally {
+ afterClassIfNeeded();
+ }
+ }
+
+ @LargeTest
+ public void testPerformAccessibilityActionClearFocus() throws Exception {
+ beforeClassIfNeeded();
+ try {
+ // bring up the activity
+ getActivity();
+
+ // find a view and make sure it is not focused
+ AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+ R.id.button5);
+ assertFalse(button.isFocused());
+
+ // focus the view
+ assertTrue(button.performAction(ACTION_FOCUS));
+
+ // find the view again and make sure it is focused
+ button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+ assertTrue(button.isFocused());
+
+ // unfocus the view
+ assertTrue(button.performAction(ACTION_CLEAR_FOCUS));
+
+ // find the view again and make sure it is not focused
+ button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+ assertFalse(button.isFocused());
+ } finally {
+ afterClassIfNeeded();
+ }
+ }
+
+ @LargeTest
+ public void testPerformAccessibilityActionSelect() throws Exception {
+ beforeClassIfNeeded();
+ try {
+ // bring up the activity
+ getActivity();
+
+ // find a view and make sure it is not selected
+ AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+ R.id.button5);
+ assertFalse(button.isSelected());
+
+ // select the view
+ assertTrue(button.performAction(ACTION_SELECT));
+
+ // find the view again and make sure it is selected
+ button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+ assertTrue(button.isSelected());
+ } finally {
+ afterClassIfNeeded();
+ }
+ }
+
+ @LargeTest
+ public void testPerformAccessibilityActionClearSelection() throws Exception {
+ beforeClassIfNeeded();
+ try {
+ // bring up the activity
+ getActivity();
+
+ // find a view and make sure it is not selected
+ AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+ R.id.button5);
+ assertFalse(button.isSelected());
+
+ // select the view
+ assertTrue(button.performAction(ACTION_SELECT));
+
+ // find the view again and make sure it is selected
+ button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+ assertTrue(button.isSelected());
+
+ // unselect the view
+ assertTrue(button.performAction(ACTION_CLEAR_SELECTION));
+
+ // find the view again and make sure it is not selected
+ button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+ assertFalse(button.isSelected());
+ } finally {
+ afterClassIfNeeded();
+ }
+ }
+
+ @LargeTest
+ public void testAccessibilityEventGetSource() throws Exception {
+ beforeClassIfNeeded();
+ try {
+ // bring up the activity
+ getActivity();
+
+ // find a view and make sure it is not focused
+ AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+ R.id.button5);
+ assertFalse(button.isSelected());
+
+ // focus the view
+ assertTrue(button.performAction(ACTION_FOCUS));
+ SystemClock.sleep(200);
+
+ // check that last event source
+ AccessibilityNodeInfo source = sLastAccessibilityEvent.getSource();
+ assertNotNull(source);
+
+ // bounds
+ Rect buttonBounds = new Rect();
+ button.getBounds(buttonBounds);
+ Rect sourceBounds = new Rect();
+ source.getBounds(sourceBounds);
+
+ assertEquals(buttonBounds.left, sourceBounds.left);
+ assertEquals(buttonBounds.right, sourceBounds.right);
+ assertEquals(buttonBounds.top, sourceBounds.top);
+ assertEquals(buttonBounds.bottom, sourceBounds.bottom);
+
+ // char sequence attributes
+ assertEquals(button.getPackageName(), source.getPackageName());
+ assertEquals(button.getClassName(), source.getClassName());
+ assertEquals(button.getText(), source.getText());
+ assertSame(button.getContentDescription(), source.getContentDescription());
+
+ // boolean attributes
+ assertSame(button.isFocusable(), source.isFocusable());
+ assertSame(button.isClickable(), source.isClickable());
+ assertSame(button.isEnabled(), source.isEnabled());
+ assertNotSame(button.isFocused(), source.isFocused());
+ assertSame(button.isLongClickable(), source.isLongClickable());
+ assertSame(button.isPassword(), source.isPassword());
+ assertSame(button.isSelected(), source.isSelected());
+ assertSame(button.isCheckable(), source.isCheckable());
+ assertSame(button.isChecked(), source.isChecked());
+ } finally {
+ afterClassIfNeeded();
+ }
+ }
+
+ @Override
+ protected void scrubClass(Class<?> testCaseClass) {
+ /* intentionally do not scrub */
+ }
+
+ /**
+ * Sets accessibility in a given state by writing the state to the
+ * settings and waiting until the accessibility manager service picks
+ * it up for max {@link #TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING}.
+ *
+ * @param state The accessibility state.
+ * @throws Exception If any error occurs.
+ */
+ private void ensureAccessibilityState(boolean state) throws Exception {
+ Context context = getInstrumentation().getContext();
+ // If the local manager ready => nothing to do.
+ AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(context);
+ if (accessibilityManager.isEnabled() == state) {
+ return;
+ }
+ synchronized (this) {
+ // Check if the system already knows about the desired state.
+ final boolean currentState = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED) == 1;
+ if (currentState != state) {
+ // Make sure we wake ourselves as the desired state is propagated.
+ accessibilityManager.addAccessibilityStateChangeListener(
+ new AccessibilityManager.AccessibilityStateChangeListener() {
+ public void onAccessibilityStateChanged(boolean enabled) {
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+ });
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, state ? 1 : 0);
+ }
+ // No while one attempt and that is it.
+ try {
+ wait(TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ if (accessibilityManager.isEnabled() != state) {
+ throw new IllegalStateException("Could not set accessibility state to: " + state);
+ }
+ }
+
+ /**
+ * Execute some set up code before any test method.
+ *
+ * NOTE: I miss Junit4's @BeforeClass
+ *
+ * @throws Exception If an error occurs.
+ */
+ private void beforeClassIfNeeded() throws Exception {
+ sExecutedTestMethodCount++;
+ if (sExecutedTestMethodCount == 1) {
+ ensureAccessibilityState(true);
+ }
+ }
+
+ /**
+ * Execute some clean up code after all test methods.
+ *
+ * NOTE: I miss Junit4's @AfterClass
+ *
+ * @throws Exception If an error occurs.
+ */
+ public void afterClassIfNeeded() throws Exception {
+ if (sExecutedTestMethodCount == sTestMethodCount) {
+ sExecutedTestMethodCount = 0;
+ ensureAccessibilityState(false);
+ }
+ }
+
+ private static IAccessibilityServiceConnection getConnection() throws Exception {
+ if (sConnection == null) {
+ IEventListener listener = new IEventListener.Stub() {
+ public void setConnection(IAccessibilityServiceConnection connection)
+ throws RemoteException {
+ AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+ info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+ info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
+ info.notificationTimeout = 0;
+ info.flags = AccessibilityServiceInfo.DEFAULT;
+ connection.setServiceInfo(info);
+ }
+
+ public void onInterrupt() {}
+
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ sLastAccessibilityEvent= AccessibilityEvent.obtain(event);
+ }
+ };
+ IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
+ ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
+ sConnection = manager.registerEventListener(listener);
+ }
+ return sConnection;
+ }
+
+ /**
+ * @return The number of test methods.
+ */
+ private int getTestMethodCount() {
+ int testMethodCount = 0;
+ for (Method method : getClass().getMethods()) {
+ final int modifiers = method.getModifiers();
+ if (method.getName().startsWith("test")
+ && (modifiers & Modifier.PUBLIC) != 0
+ && (modifiers & Modifier.STATIC) == 0) {
+ testMethodCount++;
+ }
+ }
+ return testMethodCount;
+ }
+}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index eb63c0d..0b72c3c 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -16,8 +16,6 @@
package android.net;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.NetworkStatsHistory.UID_ALL;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -51,7 +49,7 @@
public void testRecordSingleBucket() throws Exception {
final long BUCKET_SIZE = HOUR_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
// record data into narrow window to get single bucket
stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L);
@@ -62,7 +60,7 @@
public void testRecordEqualBuckets() throws Exception {
final long bucketDuration = HOUR_IN_MILLIS;
- stats = buildStats(bucketDuration);
+ stats = new NetworkStatsHistory(bucketDuration);
// split equally across two buckets
final long recordStart = TEST_START + (bucketDuration / 2);
@@ -75,7 +73,7 @@
public void testRecordTouchingBuckets() throws Exception {
final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
// split almost completely into middle bucket, but with a few minutes
// overlap into neighboring buckets. total record is 20 minutes.
@@ -94,7 +92,7 @@
public void testRecordGapBuckets() throws Exception {
final long BUCKET_SIZE = HOUR_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
// record some data today and next week with large gap
final long firstStart = TEST_START;
@@ -122,7 +120,7 @@
public void testRecordOverlapBuckets() throws Exception {
final long BUCKET_SIZE = HOUR_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
// record some data in one bucket, and another overlapping buckets
stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 256L, 256L);
@@ -135,9 +133,80 @@
assertBucket(stats, 1, 512L, 512L);
}
+ public void testRecordEntireGapIdentical() throws Exception {
+ final long[] total = new long[2];
+
+ // first, create two separate histories far apart
+ final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L);
+
+ final long TEST_START_2 = TEST_START + DAY_IN_MILLIS;
+ final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L);
+
+ // combine together with identical bucket size
+ stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ stats.recordEntireHistory(stats1);
+ stats.recordEntireHistory(stats2);
+
+ // first verify that totals match up
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 3000L, 1500L);
+
+ // now inspect internal buckets
+ assertBucket(stats, 0, 1000L, 500L);
+ assertBucket(stats, 1, 1000L, 500L);
+ assertBucket(stats, 2, 500L, 250L);
+ assertBucket(stats, 3, 500L, 250L);
+ }
+
+ public void testRecordEntireOverlapVaryingBuckets() throws Exception {
+ final long[] total = new long[2];
+
+ // create history just over hour bucket boundary
+ final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L);
+
+ final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS;
+ final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS);
+ stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L);
+
+ // combine together with minute bucket size
+ stats = new NetworkStatsHistory(MINUTE_IN_MILLIS);
+ stats.recordEntireHistory(stats1);
+ stats.recordEntireHistory(stats2);
+
+ // first verify that totals match up
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 650L, 650L);
+
+ // now inspect internal buckets
+ assertBucket(stats, 0, 10L, 10L);
+ assertBucket(stats, 1, 20L, 20L);
+ assertBucket(stats, 2, 20L, 20L);
+ assertBucket(stats, 3, 20L, 20L);
+ assertBucket(stats, 4, 20L, 20L);
+ assertBucket(stats, 5, 20L, 20L);
+ assertBucket(stats, 6, 10L, 10L);
+
+ // now combine using 15min buckets
+ stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4);
+ stats.recordEntireHistory(stats1);
+ stats.recordEntireHistory(stats2);
+
+ // first verify that totals match up
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 650L, 650L);
+
+ // and inspect buckets
+ assertBucket(stats, 0, 200L, 200L);
+ assertBucket(stats, 1, 150L, 150L);
+ assertBucket(stats, 2, 150L, 150L);
+ assertBucket(stats, 3, 150L, 150L);
+ }
+
public void testRemove() throws Exception {
- final long BUCKET_SIZE = HOUR_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
// record some data across 24 buckets
stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L);
@@ -165,13 +234,44 @@
assertEquals(0, stats.bucketCount);
}
+ public void testTotalData() throws Exception {
+ final long BUCKET_SIZE = HOUR_IN_MILLIS;
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
+
+ // record uniform data across day
+ stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L);
+
+ final long[] total = new long[2];
+
+ // verify that total outside range is 0
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, total);
+ assertTotalEquals(total, 0, 0);
+
+ // verify total in first hour
+ stats.getTotalData(TEST_START, TEST_START + HOUR_IN_MILLIS, total);
+ assertTotalEquals(total, 100, 200);
+
+ // verify total across 1.5 hours
+ stats.getTotalData(TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), total);
+ assertTotalEquals(total, 150, 300);
+
+ // verify total beyond end
+ stats.getTotalData(TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 100, 200);
+
+ // verify everything total
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 2400, 4800);
+
+ }
+
@Suppress
public void testFuzzing() throws Exception {
try {
// fuzzing with random events, looking for crashes
final Random r = new Random();
for (int i = 0; i < 500; i++) {
- stats = buildStats(r.nextLong());
+ stats = new NetworkStatsHistory(r.nextLong());
for (int j = 0; j < 10000; j++) {
if (r.nextBoolean()) {
// add range
@@ -191,10 +291,6 @@
}
}
- private static NetworkStatsHistory buildStats(long bucketSize) {
- return new NetworkStatsHistory(TYPE_MOBILE, null, UID_ALL, bucketSize);
- }
-
private static void assertConsistent(NetworkStatsHistory stats) {
// verify timestamps are monotonic
for (int i = 1; i < stats.bucketCount; i++) {
@@ -202,6 +298,11 @@
}
}
+ private static void assertTotalEquals(long[] total, long rx, long tx) {
+ assertEquals("unexpected rx", rx, total[0]);
+ assertEquals("unexpected tx", tx, total[1]);
+ }
+
private static void assertBucket(NetworkStatsHistory stats, int index, long rx, long tx) {
assertEquals("unexpected rx", rx, stats.rx[index]);
assertEquals("unexpected tx", tx, stats.tx[index]);
diff --git a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
index ee89414..ee4ce95 100644
--- a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
+++ b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
@@ -16,6 +16,10 @@
package android.net.http;
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.RecordedRequest;
+import com.google.mockwebserver.SocketPolicy;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
@@ -33,10 +37,6 @@
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLSocketFactory;
-import tests.http.MockResponse;
-import tests.http.MockWebServer;
-import tests.http.RecordedRequest;
-import tests.http.SocketPolicy;
public abstract class AbstractProxyTest extends TestCase {
diff --git a/core/tests/coretests/src/android/net/http/CookiesTest.java b/core/tests/coretests/src/android/net/http/CookiesTest.java
index e736bc9..29e590f 100644
--- a/core/tests/coretests/src/android/net/http/CookiesTest.java
+++ b/core/tests/coretests/src/android/net/http/CookiesTest.java
@@ -16,6 +16,9 @@
package android.net.http;
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.RecordedRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
@@ -30,9 +33,6 @@
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
-import tests.http.MockResponse;
-import tests.http.MockWebServer;
-import tests.http.RecordedRequest;
public final class CookiesTest extends TestCase {
diff --git a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
index ad3ec3d..da77298 100644
--- a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
+++ b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
@@ -16,6 +16,12 @@
package android.net.http;
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.SocketPolicy;
+import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
+import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
+import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
@@ -24,12 +30,6 @@
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
-import tests.http.MockResponse;
-import tests.http.MockWebServer;
-import tests.http.SocketPolicy;
-import static tests.http.SocketPolicy.DISCONNECT_AT_END;
-import static tests.http.SocketPolicy.SHUTDOWN_INPUT_AT_END;
-import static tests.http.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
public final class DefaultHttpClientTest extends TestCase {
diff --git a/core/tests/coretests/src/android/view/GravityTest.java b/core/tests/coretests/src/android/view/GravityTest.java
index 180a390..2a7a64f 100644
--- a/core/tests/coretests/src/android/view/GravityTest.java
+++ b/core/tests/coretests/src/android/view/GravityTest.java
@@ -59,11 +59,11 @@
assertOneGravity(Gravity.CLIP_VERTICAL, Gravity.CLIP_VERTICAL, false);
assertOneGravity(Gravity.CLIP_VERTICAL, Gravity.CLIP_VERTICAL, true);
- assertOneGravity(Gravity.LEFT, Gravity.BEFORE, false);
- assertOneGravity(Gravity.RIGHT, Gravity.BEFORE, true);
+ assertOneGravity(Gravity.LEFT, Gravity.START, false);
+ assertOneGravity(Gravity.RIGHT, Gravity.START, true);
- assertOneGravity(Gravity.RIGHT, Gravity.AFTER, false);
- assertOneGravity(Gravity.LEFT, Gravity.AFTER, true);
+ assertOneGravity(Gravity.RIGHT, Gravity.END, false);
+ assertOneGravity(Gravity.LEFT, Gravity.END, true);
}
private void assertOneGravity(int expected, int initial, boolean isRtl) {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 5ed7966..b9c0d80 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -132,6 +132,7 @@
<assign-permission name="android.permission.BATTERY_STATS" uid="shell" />
<assign-permission name="android.permission.INTERNAL_SYSTEM_WINDOW" uid="shell" />
<assign-permission name="android.permission.INJECT_EVENTS" uid="shell" />
+ <assign-permission name="android.permission.RETRIEVE_WINDOW_CONTENT" uid="shell" />
<assign-permission name="android.permission.SET_ACTIVITY_WATCHER" uid="shell" />
<assign-permission name="android.permission.READ_INPUT_STATE" uid="shell" />
<assign-permission name="android.permission.SET_ORIENTATION" uid="shell" />
diff --git a/docs/html/guide/topics/graphics/renderscript.jd b/docs/html/guide/topics/graphics/renderscript.jd
index 60bffdb..180322f 100644
--- a/docs/html/guide/topics/graphics/renderscript.jd
+++ b/docs/html/guide/topics/graphics/renderscript.jd
@@ -630,7 +630,7 @@
<dt><code>ScriptC_helloworld</code></dt>
<dd>This class is generated by the Android build tools and is the reflected version of the
- <code>helloworld.rs</code> Renderscript. It provides a a high level entry point into the
+ <code>helloworld.rs</code> Renderscript. It provides a high level entry point into the
<code>helloworld.rs</code> native code by defining the corresponding methods that you can call
from the traditional framework APIs.</dd>
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 9ac1a00..3c8aba3 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -100,7 +100,6 @@
public static final int USAGE_GRAPHICS_CONSTANTS = 0x0008;
/**
- * @hide
* USAGE_GRAPHICS_RENDER_TARGET The allcation will be used as a
* target for offscreen rendering
*
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index dc5fc84..83d4f6d 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -263,7 +263,7 @@
// direction is not affected by the rotation or mirroring of
// CAMERA_CMD_SET_DISPLAY_ORIENTATION. Coordinates range from -1000 to 1000.
// (-1000,-1000) is the upper left point. (1000, 1000) is the lower right
- // point. The length and width of focus areas cannot be 0 or negative.
+ // point. The width and height of focus areas cannot be 0 or negative.
//
// The fifth element is the weight. Values for weight must range from 1 to
// 1000. The weight should be interpreted as a per-pixel weight - all
@@ -373,7 +373,7 @@
// direction is not affected by the rotation or mirroring of
// CAMERA_CMD_SET_DISPLAY_ORIENTATION. Coordinates of the rectangle range
// from -1000 to 1000. (-1000, -1000) is the upper left point. (1000, 1000)
- // is the lower right point. The length and width of metering areas cannot
+ // is the lower right point. The width and height of metering areas cannot
// be 0 or negative.
//
// The fifth element is the weight. Values for weight must range from 1 to
@@ -550,6 +550,7 @@
static const char PIXEL_FORMAT_YUV422I[]; // YUY2
static const char PIXEL_FORMAT_YUV420P[]; // YV12
static const char PIXEL_FORMAT_RGB565[];
+ static const char PIXEL_FORMAT_RGBA8888[];
static const char PIXEL_FORMAT_JPEG[];
// Values for focus mode settings.
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 3b5aba4..c9f694a 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -655,11 +655,6 @@
// Oldest sample to consider when calculating the velocity.
static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
- // When the total duration of the window of samples being averaged is less
- // than the window size, the resulting velocity is scaled to reduce the impact
- // of overestimation in short traces.
- static const nsecs_t MIN_WINDOW = 100 * 1000000; // 100 ms
-
// The minimum duration between samples when estimating velocity.
static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 6b69b8a..fd6c22c 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -20,8 +20,19 @@
import android.content.Context;
import android.content.Intent;
import android.util.Log;
-
+import com.android.org.bouncycastle.openssl.PEMReader;
+import com.android.org.bouncycastle.openssl.PEMWriter;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.Charsets;
import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.List;
/**
* {@hide}
@@ -60,6 +71,40 @@
/** Data type for PKCS12. */
public static final String PKCS12 = "PKCS12";
+ /**
+ * Convert objects to a PEM format, which is used for
+ * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY
+ * entries.
+ */
+ public static byte[] convertToPem(Object... objects) throws IOException {
+ ByteArrayOutputStream bao = new ByteArrayOutputStream();
+ Writer writer = new OutputStreamWriter(bao, Charsets.US_ASCII);
+ PEMWriter pw = new PEMWriter(writer);
+ for (Object o : objects) {
+ pw.writeObject(o);
+ }
+ pw.close();
+ return bao.toByteArray();
+ }
+ /**
+ * Convert objects from PEM format, which is used for
+ * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY
+ * entries.
+ */
+ public static List<Object> convertFromPem(byte[] bytes) throws IOException {
+ ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
+ Reader reader = new InputStreamReader(bai, Charsets.US_ASCII);
+ PEMReader pr = new PEMReader(reader);
+
+ List<Object> result = new ArrayList<Object>();
+ Object o;
+ while ((o = pr.readObject()) != null) {
+ result.add(o);
+ }
+ pr.close();
+ return result;
+ }
+
private static Credentials singleton;
public static Credentials getInstance() {
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index ec820cf..ba784ed 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -34,6 +34,7 @@
import java.io.Closeable;
import java.io.IOException;
import java.security.KeyFactory;
+import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
@@ -185,11 +186,9 @@
throw new IllegalArgumentException("bytes == null");
}
try {
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
- } catch (NoSuchAlgorithmException e) {
- throw new AssertionError(e);
- } catch (InvalidKeySpecException e) {
+ KeyPair keyPair = (KeyPair) Credentials.convertFromPem(bytes).get(0);
+ return keyPair.getPrivate();
+ } catch (IOException e) {
throw new AssertionError(e);
}
}
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index c295315..9392cf2 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -146,6 +146,7 @@
const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv";
const char CameraParameters::PIXEL_FORMAT_YUV420P[] = "yuv420p";
const char CameraParameters::PIXEL_FORMAT_RGB565[] = "rgb565";
+const char CameraParameters::PIXEL_FORMAT_RGBA8888[] = "rgba8888";
const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg";
// Values for focus mode settings.
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index e95dbe4..0af7f80 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -700,7 +700,6 @@
const uint32_t VelocityTracker::HISTORY_SIZE;
const nsecs_t VelocityTracker::MAX_AGE;
-const nsecs_t VelocityTracker::MIN_WINDOW;
const nsecs_t VelocityTracker::MIN_DURATION;
VelocityTracker::VelocityTracker() {
@@ -891,14 +890,6 @@
// Make sure we used at least one sample.
if (samplesUsed != 0) {
- // Scale the velocity linearly if the window of samples is small.
- nsecs_t totalDuration = newestMovement.eventTime - oldestMovement.eventTime;
- if (totalDuration < MIN_WINDOW) {
- float scale = float(totalDuration) / float(MIN_WINDOW);
- accumVx *= scale;
- accumVy *= scale;
- }
-
*outVx = accumVx;
*outVy = accumVy;
return true;
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index a54cf28..7634c6c 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -76,8 +76,9 @@
public static final int FILE_TYPE_PNG = 33;
public static final int FILE_TYPE_BMP = 34;
public static final int FILE_TYPE_WBMP = 35;
+ public static final int FILE_TYPE_WEBP = 36;
private static final int FIRST_IMAGE_FILE_TYPE = FILE_TYPE_JPEG;
- private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WBMP;
+ private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WEBP;
// Playlist file types
public static final int FILE_TYPE_M3U = 41;
@@ -213,6 +214,7 @@
addFileType("PNG", FILE_TYPE_PNG, "image/png", MtpConstants.FORMAT_PNG);
addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", MtpConstants.FORMAT_BMP);
addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
+ addFileType("WEBP", FILE_TYPE_WBMP, "image/webp");
addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 8a180d8..6cb3847 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -26,13 +26,6 @@
#include <system/audio.h>
// ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
-// ----------------------------------------------------------------------------
namespace android {
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index ebe821f..07152d8 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -30,26 +30,6 @@
// foreground thread.
#ifndef DISABLE_GROUP_SCHEDULE_HACK
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
-
-static int myTid() {
-#ifdef HAVE_GETTID
- return gettid();
-#else
- return getpid();
-#endif
-}
-
#undef LOG_TAG
#define LOG_TAG "IMediaMetadataRetriever"
#include <utils/Log.h>
@@ -60,18 +40,18 @@
static void sendSchedPolicy(Parcel& data)
{
SchedPolicy policy;
- get_sched_policy(myTid(), &policy);
+ get_sched_policy(gettid(), &policy);
data.writeInt32(policy);
}
static void setSchedPolicy(const Parcel& data)
{
SchedPolicy policy = (SchedPolicy) data.readInt32();
- set_sched_policy(myTid(), policy);
+ set_sched_policy(gettid(), policy);
}
static void restoreSchedPolicy()
{
- set_sched_policy(myTid(), SP_FOREGROUND);
+ set_sched_policy(gettid(), SP_FOREGROUND);
}
}; // end namespace android
#endif
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index 88157d2..8b953e0 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -23,13 +23,6 @@
#include <media/JetPlayer.h>
-#ifdef HAVE_GETTID
-static pid_t myTid() { return gettid(); }
-#else
-static pid_t myTid() { return getpid(); }
-#endif
-
-
namespace android
{
@@ -188,7 +181,7 @@
// signal main thread that we started
{
Mutex::Autolock l(mMutex);
- mTid = myTid();
+ mTid = gettid();
LOGV("JetPlayer::render(): render thread(%d) signal", mTid);
mCondition.signal();
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index eae93ff..54a6547 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -68,18 +68,6 @@
#include <OMX.h>
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
-
namespace {
using android::media::Metadata;
using android::status_t;
@@ -387,14 +375,6 @@
return NO_ERROR;
}
-static int myTid() {
-#ifdef HAVE_GETTID
- return gettid();
-#else
- return getpid();
-#endif
-}
-
status_t MediaPlayerService::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
@@ -426,7 +406,7 @@
}
result.append(" Files opened and/or mapped:\n");
- snprintf(buffer, SIZE, "/proc/%d/maps", myTid());
+ snprintf(buffer, SIZE, "/proc/%d/maps", gettid());
FILE *f = fopen(buffer, "r");
if (f) {
while (!feof(f)) {
@@ -446,13 +426,13 @@
result.append("\n");
}
- snprintf(buffer, SIZE, "/proc/%d/fd", myTid());
+ snprintf(buffer, SIZE, "/proc/%d/fd", gettid());
DIR *d = opendir(buffer);
if (d) {
struct dirent *ent;
while((ent = readdir(d)) != NULL) {
if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
- snprintf(buffer, SIZE, "/proc/%d/fd/%s", myTid(), ent->d_name);
+ snprintf(buffer, SIZE, "/proc/%d/fd/%s", gettid(), ent->d_name);
struct stat s;
if (lstat(buffer, &s) == 0) {
if ((s.st_mode & S_IFMT) == S_IFLNK) {
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 8f776b4..06fb103 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -40,18 +40,6 @@
#include "MetadataRetrieverClient.h"
#include "StagefrightMetadataRetriever.h"
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
-
namespace android {
extern player_type getPlayerType(const char* url);
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
index 589c625..7e04523 100644
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ b/media/libmediaplayerservice/MidiFile.cpp
@@ -29,17 +29,12 @@
#include <libsonivox/eas_reverb.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <unistd.h>
#include <system/audio.h>
#include "MidiFile.h"
-#ifdef HAVE_GETTID
-static pid_t myTid() { return gettid(); }
-#else
-static pid_t myTid() { return getpid(); }
-#endif
-
// ----------------------------------------------------------------------------
namespace android {
@@ -455,7 +450,7 @@
// signal main thread that we started
{
Mutex::Autolock l(mMutex);
- mTid = myTid();
+ mTid = gettid();
LOGV("render thread(%d) signal", mTid);
mCondition.signal();
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f6a8b17..d0f6730 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -2862,8 +2862,8 @@
* For historical reasons, any user data start
* with "\0xA9", must be followed by its assoicated
* language code.
- * 0x0012: locale en
- * 0x15c7: language 5575
+ * 0x0012: text string length
+ * 0x15c7: lang (locale) code: en
*/
writeInt32(0x001215c7);
writeLatitude(mLatitudex10000);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
index 79fd2cb..e7f98de 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
@@ -123,63 +123,6 @@
intent.setDataAndType(path, mimetype);
startActivity(intent);
}
-
- @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_0:
- MediaPlayer mp = new MediaPlayer();
- try{
- mp.setDataSource(MediaNames.VIDEO_RTSP3GP);
- Log.v("emily","awb " + testfilepath);
- mp.setDisplay(mSurfaceView.getHolder());
- mp.prepare();
- mp.start();
- }catch (Exception e){}
- break;
-
- //start the music player intent with the test URL from PV
- case KeyEvent.KEYCODE_1:
- startPlayback(MediaNames.STREAM_MP3_1);
- break;
-
- case KeyEvent.KEYCODE_2:
- startPlayback(MediaNames.STREAM_MP3_2);
- break;
-
- case KeyEvent.KEYCODE_3:
- startPlayback(MediaNames.STREAM_MP3_3);
- break;
-
- case KeyEvent.KEYCODE_4:
- startPlayback(MediaNames.STREAM_MP3_4);
- break;
-
- case KeyEvent.KEYCODE_5:
- startPlayback(MediaNames.STREAM_MP3_5);
- break;
-
- case KeyEvent.KEYCODE_6:
- startPlayback(MediaNames.STREAM_MP3_6);
- break;
-
- case KeyEvent.KEYCODE_7:
- startPlayback(MediaNames.STREAM_MP3_7);
- break;
-
- case KeyEvent.KEYCODE_8:
- startPlayback(MediaNames.STREAM_MP3_8);
- break;
-
- case KeyEvent.KEYCODE_9:
- startPlayback(MediaNames.STREAM_MP3_9);
- break;
-
-
-
- }
- return super.onKeyDown(keyCode, event);
-
- }
public static boolean checkStreamingServer() throws Exception {
InetAddress address = InetAddress.getByAddress(MediaNames.STREAM_SERVER);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index ca6e999..d6e1346 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -50,19 +50,9 @@
public static final long PAUSE_WAIT_TIME = 3000;
public static final long WAIT_TIME = 2000;
public static final long WAIT_SNAPSHOT_TIME = 5000;
-
- //Streaming Video
- public static final String VIDEO_HTTP3GP = "http://pvs.pv.com/jj/lipsync0.3gp";
- public static final String VIDEO_RTSP3GP = "rtsp://63.241.31.203/public/jj/md.3gp";
- public static final String VIDEO_RTSP3GP2 = "rtsp://pvs.pv.com/public/live_dvd1.3gp";
- public static final String VIDEO_RTSP3GP3 =
- "rtsp://ehug.rtsp-youtube.l.google.com/" +
- "Ci4LENy73wIaJQmeRVCJq4HuQBMYDSANFEIJbXYtZ29vZ2xlSARSB2RldGFpbHMM/0/0/0/video.3gp";
- //public static final String VIDEO_RTSP3GP = "rtsp://193.159.241.21/sp/alizee05.3gp";
-
+
//local video
public static final String VIDEO_MP4 = "/sdcard/media_api/video/MPEG4_320_AAC_64.mp4";
- public static final String VIDEO_LONG_3GP = "/sdcard/media_api/video/radiohead.3gp";
public static final String VIDEO_SHORT_3GP = "/sdcard/media_api/video/short.3gp";
public static final String VIDEO_LARGE_SIZE_3GP = "/sdcard/media_api/video/border_large.3gp";
public static final String VIDEO_H263_AAC = "/sdcard/media_api/video/H263_56_AAC_24.3gp";
@@ -73,205 +63,9 @@
public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp";
public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp";
- //ringtone
- public static final String ringtone = "/sdcard/media_api/ringtones/F1_NewVoicemail.mp3";
-
- //streaming mp3
- public static final String STREAM_MP3_1 =
- "http://wms.pv.com:7070/MediaDownloadContent/mp3/chadthi_jawani_128kbps.mp3";
- public static final String STREAM_MP3_2 =
- "http://wms.pv.com:7070/MediaDownloadContent/mp3/dualStereo.mp3";
- public static final String STREAM_MP3_3 =
- "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/15%20Keep%20Holding%20On.mp3";
- public static final String STREAM_MP3_4 =
- "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/1%20-%20Apologize.mp3";
- public static final String STREAM_MP3_5 =
- "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/" +
- "03%20You're%20Gonna%20Miss%20This.mp3";
- public static final String STREAM_MP3_6 =
- "http://wms.pv.com:7070/mediadownloadcontent/UserUploads" +
- "/02%20Looney%20Tunes%20%C3%82%C2%B7%20Light%20Cavalry%20Overture%20(LP%20Version).mp3";
- public static final String STREAM_MP3_7 =
- "http://wms.pv.com:7070/mediadownloadcontent/UserUploads" +
- "/01%20Love%20Song%20(Album%20Version).mp3";
- public static final String STREAM_MP3_8 =
- "http://wms.pv.com:7070/MediaDownloadContent/UserUploads/1%20-%20Apologize.mp3";
- public static final String STREAM_MP3_9 =
- "http://wms.pv.com:7070/MediaDownloadContent/UserUploads" +
- "/1%20-%20Smile%20(Explicit%20Version).mp3";
- public static final String STREAM_MP3_10 =
- "http://wms.pv.com:7070/MediaDownloadContent/UserUploads/beefcake.mp3";
-
- //Sonivox
- public static String MIDIFILES[] = {
- "/sdcard/media_api/music/Leadsol.mxmf",
- "/sdcard/media_api/music/abba.imy", "/sdcard/media_api/music/ants.mid",
- "/sdcard/media_api/music/greensleeves.rtttl", "/sdcard/media_api/music/test.ota"};
-
- //Performance measurement
- public static String[] WAVFILES = {
- "/sdcard/media_api/music_perf/WAV/M1F1-AlawWE-AFsp.wav",
- "/sdcard/media_api/music_perf/WAV/M1F1-float64-AFsp.wav",
- "/sdcard/media_api/music_perf/WAV/song.wav",
- "/sdcard/media_api/music_perf/WAV/WAVEtest.wav",
- "/sdcard/media_api/music_perf/WAV/WAVEtest_out.wav",
- "/sdcard/media_api/music_perf/WAV/test_out.wav"};
-
- public static String[] AMRNBFILES = {
- "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.9kbps_6.24kbps_8khz_mono_NMC.amr",
- "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.15kbps_5.46kbps_8khz_mono_NMC.amr",
- "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.4kbps_7.80kbps_8khz_mono_NMC.amr",
- "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.95kbps_9.6kbps_8khz_mono_NMC.amr",
- "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_10.2kbps_10.48kbps_8khz_mono_NMC.amr"};
-
- public static String[] AMRWBFILES = {
- "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_15.85kbps_16kbps.amr",
- "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_18.25kbps_18kbps.amr",
- "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_19.85kbps_20kbps.amr",
- "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.05kbps_23kbps.amr",
- "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.85kbps_24kbps.amr",
- "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_19.85kbps_20kbps.amr",
- "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.05kbps_23kbps.amr",
- "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.85kbps_24kbps.amr",
- "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.05kbps_23kbps.amr",
- "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.85kbps_24kbps.amr", };
-
- public static String[] MP3FILES = {
- "/sdcard/media_api/music_perf/MP3/NIN_56kbps_32khz_stereo_VBR_MCA.MP3",
- "/sdcard/media_api/music_perf/MP3/NIN_80kbps_32khz_stereo_VBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_80kbps_44.1khz_stereo_VBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_80kbps_48khz_stereo_VBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_112kbps_32khz_stereo_VBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_112kbps_44.1khz_stereo_VBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_112kbps_48khz_stereo_VBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_192kbps_32khz_mono_CBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_192kbps_44.1khz_mono_CBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_192kbps_48khz_mono_CBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_256kbps_44.1khz_mono_CBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/NIN_256kbps_48khz_mono_CBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/PD_112kbps_32khz_stereo_VBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/PD_112kbps_44.1khz_stereo_VBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/PD_112kbps_48khz_stereo_VBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/PD_192kbps_32khz_mono_CBR_DPA.mp3",
- "/sdcard/media_api/music_perf/MP3/PD_256kbps_44.1khz_mono_CBR_DPA.mp3",
- "/sdcard/media_api/music_perf/MP3/PD_256kbps_48khz_mono_CBR_MCA.mp3",
- "/sdcard/media_api/music_perf/MP3/WC_256kbps_44.1khz_mono_CBR_DPA.mp3",
- "/sdcard/media_api/music_perf/MP3/WC_256kbps_48khz_mono_CBR_DPA.mp3",
- "/sdcard/media_api/music_perf/regular_album_photo/Apologize.mp3",
- "/sdcard/media_api/music_perf/regular_album_photo/Because_Of_You.mp3",
- "/sdcard/media_api/music_perf/regular_album_photo/Complicated.mp3",
- "/sdcard/media_api/music_perf/regular_album_photo/Glamorous.mp3",
- "/sdcard/media_api/music_perf/regular_album_photo/Im_With_You.mp3",
- "/sdcard/media_api/music_perf/regular_album_photo/Smile.mp3",
- "/sdcard/media_api/music_perf/regular_album_photo/Suddenly_I_See.mp3",
- "/sdcard/media_api/music_perf/regular_album_photo/When You Say Nothing At All.mp3",
- "/sdcard/media_api/music_perf/regular_album_photo/my_happy_ending.mp3"};
-
- public static String[] AACFILES = {
- "/sdcard/media_api/music_perf/AAC/AI_AAC_24kbps_12khz_Mono_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
- "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
- };
-
- public static String[] VIDEOFILES = { "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_10fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
- "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_12fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
- "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_15fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
- "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
- "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_SSE.mp4",
- "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_7.5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
- "/sdcard/media_api/video_perf/AI_WMV_1024kbps_20fps_QCIF_176x144_noaudio_SSE.wmv",
- "/sdcard/media_api/video_perf/AI_WMV_1024kbps_25fps_QCIF_176x144_noaudio_SSE.wmv",
- "/sdcard/media_api/video_perf/Chicken.wmv",
- "/sdcard/media_api/video_perf/MP_qcif_15fps_100kbps_48kHz_192kbps_30secs.wmv",
- "/sdcard/media_api/video_perf/NIN_CTO_H264_123kbps_5fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_10.2fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_12fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_15fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_mono_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_10fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_12fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_15fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_7.5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_H263_128kbps_10fps_QCIF_174x144_noaudio_SSE.mp4",
- "/sdcard/media_api/video_perf/NIN_H263_128kbps_15fps_QCIF_174x144_noaudio_SSE.mp4",
- "/sdcard/media_api/video_perf/NIN_H263_48kbps_10fps_QCIF_174x144_noaudio_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_H263_48kbps_12fps_QCIF_174x144_noaudio_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_H264_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp",
- "/sdcard/media_api/video_perf/NIN_H264_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp",
- "/sdcard/media_api/video_perf/PV_H264_2000kbps_20fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4",
- "/sdcard/media_api/video_perf/PV_H264_2000kbps_25fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4",
- "/sdcard/media_api/video_perf/PV_H264_2000kbps_30fps_CIF_352x288+AAC_128kbps_48khz_stereo_SSE.mp4",
- "/sdcard/media_api/video_perf/Stevie-1.wmv",
- "/sdcard/media_api/video_perf/WC_H264_1600kbps_20fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4",
- "/sdcard/media_api/video_perf/WC_H264_1600kbps_25fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4",
- "/sdcard/media_api/video_perf/WC_H264_1600kbps_30fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4",
- "/sdcard/media_api/video_perf/bugs.wmv",
- "/sdcard/media_api/video_perf/niceday.wmv",
- "/sdcard/media_api/video_perf/eaglesatopnflpe.wmv",
-
- };
-
- //wma - only support up to wma 9
- public static String[] WMASUPPORTED = {
- "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma"
-
- };
-
- public static String[] WMAUNSUPPORTED = {
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_127kbps_48khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_44.1khz_stereo_2pVBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_48khz_stereo_2pVBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_88khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_96khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_44.1khz_stereo_2pVBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_88khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_96khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_44khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_48khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_88khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_96khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_44khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_48khz_stereo_CBR_DPA.wma",
- "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_88khz_stereo_CBR_DPA.wma"
- };
-
//Media Recorder
public static final String RECORDER_OUTPUT = "/sdcard/media_api/recorderOutput.amr";
-
+
//video thumbnail
public static final String THUMBNAIL_OUTPUT = "/sdcard/media_api/videoThumbnail.png";
public static final String GOLDEN_THUMBNAIL_OUTPUT = "/sdcard/media_api/goldenThumbnail.png";
@@ -318,7 +112,7 @@
"/sdcard/media_api/metadata/test34.wmv",
"/sdcard/media_api/metadata/test_metadata.mp4",
};
-
+
public static final String[] METADATA_RETRIEVAL_TEST_FILES = {
// Raw AAC is not supported
// "/sdcard/media_api/test_raw.aac",
@@ -405,54 +199,8 @@
null, null, null, null, null, null, "295", "1", null}
};
- public static final String META_DATA_OTHERS [][] = {
- {"/sdcard/media_api/metaDataTestMedias/3GP/cat.3gp", null, null, null,
- null, null, "20080309T002415.000Z", null,
- null, null, "63916", "2", null},
- {"/sdcard/media_api/metaDataTestMedias/AMR/AMR_NB.amr", null, null, null,
- null, null, null, null,
- null, null, "126540", "1", null},
- {"/sdcard/media_api/metaDataTestMedias/AMRWB/AMR_WB.amr", null, null, null,
- null, null, null, null,
- null, null, "231180", "1", null},
- {"/sdcard/media_api/metaDataTestMedias/M4A/Jaws Of Life_ver1.m4a", "1/8", "Suspended Animation",
- "John Petrucci", null, null, "20070510T125223.000Z",
- "12", "Jaws Of Life", "2005", "449329", "1", "m4a composer"},
- {"/sdcard/media_api/metaDataTestMedias/M4V/sample_iPod.m4v", null, null,
- null, null, null, "20051220T202015.000Z",
- null, null, null, "85500", "2", null},
- {"/sdcard/media_api/metaDataTestMedias/MIDI/MIDI.mid", null, "Suspended Animation",
- "John Petrucci", null, null, "20070510T125223.000Z",
- null, null, "2005", "231180", "1", null},
- {"/sdcard/media_api/metaDataTestMedias/MP4/kung_fu_panda_h264.mp4", "2/0", "mp4 album Kung Fu Panda",
- "mp4 artist Kung Fu Panda", null, null, "20080517T091451.000Z",
- "40", "Kung Fu Panda", "2008", "128521", "2", "mp4 composer"},
- {"/sdcard/media_api/metaDataTestMedias/OGG/Ring_Classic_02.ogg", null, "Suspended Animation",
- "John Petrucci", null, null, "20070510T125223.000Z",
- null, null, "2005", "231180", "1", null},
- {"/sdcard/media_api/metaDataTestMedias/OGG/When You Say Nothing At All.ogg",
- null, "Suspended Animation", "John Petrucci",
- null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1", null},
- {"/sdcard/media_api/metaDataTestMedias/WAV/Im With You.wav", null, null,
- null, null, null, null,
- null, null, null, "224000", "1", null},
- {"/sdcard/media_api/metaDataTestMedias/WMA/WMA9.wma", "6", "Ten Songs in the Key of Betrayal",
- "Alien Crime Syndicate", "Alien Crime Syndicate",
- "wma 9 Composer", "20040521T175729.483Z",
- "Rock", "Run for the Money", "2004", "134479", "1", null},
- {"/sdcard/media_api/metaDataTestMedias/WMA/WMA10.wma", "09", "wma 10 Album",
- "wma 10 Album Artist", "wma 10 Artist", "wma 10 Composer", "20070705T063625.097Z",
- "Acid Jazz", "wma 10 Title", "2010", "126574", "1", null},
- {"/sdcard/media_api/metaDataTestMedias/WMV/bugs.wmv", "8", "wmv 9 Album",
- null, "wmv 9 Artist ", null, "20051122T155247.540Z",
- null, "Looney Tunes - Hare-Breadth Hurry", "2005", "193482", "2", null},
- {"/sdcard/media_api/metaDataTestMedias/WMV/clips_ver7.wmv", "50", "wmv 7 Album",
- null, "Hallau Shoots & Company", null, "20020226T170045.891Z",
- null, "CODEC Shootout", "1986", "43709", "2", null}
- };
-
//output recorded video
-
+
public static final String RECORDED_HVGA_H263 = "/sdcard/HVGA_H263.3gp";
public static final String RECORDED_QVGA_H263 = "/sdcard/QVGA_H263.3gp";
public static final String RECORDED_SQVGA_H263 = "/sdcard/SQVGA_H263.3gp";
@@ -474,22 +222,7 @@
public static final long RECORDED_TIME = 5000;
public static final long VALID_VIDEO_DURATION = 2000;
-
- //Videos for the mediaplayer stress test
- public static String[] H263_STRESS = {
- "/sdcard/media_api/video_stress/h263/H263_CIF.3gp",
- "/sdcard/media_api/video_stress/h263/H263_QCIF.3gp",
- "/sdcard/media_api/video_stress/h263/H263_QVGA.3gp",
- "/sdcard/media_api/video_stress/h263/H263_SQVGA.3gp"
- };
-
- public static String[] MPEG4_STRESS = {
- "/sdcard/media_api/video_stress/h263/mpeg4_CIF.mp4",
- "/sdcard/media_api/video_stress/h263/mpeg4_QCIF.3gp",
- "/sdcard/media_api/video_stress/h263/mpeg4_QVGA.3gp",
- "/sdcard/media_api/video_stress/h263/mpeg4_SQVGA.mp4"
- };
-
+
//Streaming test files
public static final byte [] STREAM_SERVER = new byte[] {(byte)75,(byte)17,(byte)48,(byte)204};
public static final String STREAM_H264_480_360_1411k =
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
index 6ded74d..00e0a52 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
@@ -47,14 +47,8 @@
CORRUPTED_ID3V2_TYER, CORRUPTED_ID3V2_TYER_2, CORRUPTED_ID3V2_TIT
}
- public static enum NON_MP3_TEST_FILE{
- THREE3GP, AMRNB, AMRWB, M4A1, M4V, MIDI,
- H264, OGG1, OGG2, WAV, WMA9, WMA10, WMV9, WMV7
- }
-
public static METADATA_EXPECTEDRESULT meta;
public static MP3_TEST_FILE mp3_test_file;
- public static NON_MP3_TEST_FILE non_mp3_test_file;
@MediumTest
public static void testID3V1V2Metadata() throws Exception {
@@ -116,88 +110,11 @@
validateMetatData(mp3_test_file.CORRUPTED_ID3V2_TIT.ordinal(), MediaNames.META_DATA_MP3);
}
- @MediumTest
- public static void test3gp_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.THREE3GP.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @MediumTest
- public static void testAmr_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.AMRNB.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @MediumTest
- public static void testAmrWb_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.AMRWB.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @MediumTest
- public static void testM4A1_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.M4A1.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @MediumTest
- public static void testM4v_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.M4V.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @MediumTest
- public static void testH264_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.H264.ordinal(), MediaNames.META_DATA_OTHERS);
- }
- //bug# 1440489
- @Suppress
- @MediumTest
- public static void testOgg1_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.OGG1.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @Suppress
- @MediumTest
- public static void testOgg2_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.OGG2.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @Suppress
- @MediumTest
- public static void testMidi_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.MIDI.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @MediumTest
- public static void testWav_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.WAV.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @Suppress
- @MediumTest
- public static void testWma9_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.WMA9.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @Suppress
- @MediumTest
- public static void testWma10_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.WMA10.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @Suppress
- @MediumTest
- public static void testWmv9_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.WMV9.ordinal(), MediaNames.META_DATA_OTHERS);
- }
-
- @Suppress
- @MediumTest
- public static void testWmv10_Metadata() throws Exception {
- validateMetatData(non_mp3_test_file.WMV7.ordinal(), MediaNames.META_DATA_OTHERS);
- }
private static void validateMetatData(int fileIndex, String meta_data_file[][]) {
Log.v(TAG, "filePath = "+ meta_data_file[fileIndex][0]);
if ((meta_data_file[fileIndex][0].endsWith("wma") && !MediaProfileReader.getWMAEnable()) ||
(meta_data_file[fileIndex][0].endsWith("wmv") && !MediaProfileReader.getWMVEnable())) {
- Log.v(TAG, "Skip test since windows media is not supported");
return;
}
String value = null;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index d22025c..3a9564d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -369,13 +369,7 @@
boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_MP4);
assertTrue("Local MP4 SeekTo", isSeek);
}
-
- @LargeTest
- public void testVideoLong3gpSeekTo() throws Exception {
- boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_LONG_3GP);
- assertTrue("Local 3gp SeekTo", isSeek);
- }
-
+
@LargeTest
public void testVideoH263AACSeekTo() throws Exception {
boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_H263_AAC);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 34affa7..3b5b9a3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -100,109 +100,6 @@
MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
}
- public void createDB() {
- mDB = SQLiteDatabase.openOrCreateDatabase("/sdcard/perf.db", null);
- mDB.execSQL("CREATE TABLE IF NOT EXISTS perfdata (_id INTEGER PRIMARY KEY," +
- "file TEXT," + "setdatatime LONG," + "preparetime LONG," +
- "playtime LONG" + ");");
- //clean the table before adding new data
- mDB.execSQL("DELETE FROM perfdata");
- }
-
- public void audioPlaybackStartupTime(String[] testFile) {
- long t1 = 0;
- long t2 = 0;
- long t3 = 0;
- long t4 = 0;
- long setDataSourceDuration = 0;
- long prepareDuration = 0;
- long startDuration = 0;
- long totalSetDataTime = 0;
- long totalPrepareTime = 0;
- long totalStartDuration = 0;
-
- int numberOfFiles = testFile.length;
- Log.v(TAG, "File length " + numberOfFiles);
- for (int k = 0; k < numberOfFiles; k++) {
- MediaPlayer mp = new MediaPlayer();
- try {
- t1 = SystemClock.uptimeMillis();
- FileInputStream fis = new FileInputStream(testFile[k]);
- FileDescriptor fd = fis.getFD();
- mp.setDataSource(fd);
- fis.close();
- t2 = SystemClock.uptimeMillis();
- mp.prepare();
- t3 = SystemClock.uptimeMillis();
- mp.start();
- t4 = SystemClock.uptimeMillis();
- } catch (Exception e) {
- Log.v(TAG, e.toString());
- }
- setDataSourceDuration = t2 - t1;
- prepareDuration = t3 - t2;
- startDuration = t4 - t3;
- totalSetDataTime = totalSetDataTime + setDataSourceDuration;
- totalPrepareTime = totalPrepareTime + prepareDuration;
- totalStartDuration = totalStartDuration + startDuration;
- mDB.execSQL("INSERT INTO perfdata (file, setdatatime, preparetime," +
- " playtime) VALUES (" + '"' + testFile[k] + '"' + ',' +
- setDataSourceDuration + ',' + prepareDuration +
- ',' + startDuration + ");");
- Log.v(TAG, "File name " + testFile[k]);
- mp.stop();
- mp.release();
- }
- Log.v(TAG, "setDataSource average " + totalSetDataTime / numberOfFiles);
- Log.v(TAG, "prepare average " + totalPrepareTime / numberOfFiles);
- Log.v(TAG, "start average " + totalStartDuration / numberOfFiles);
-
- }
-
- @Suppress
- public void testStartUpTime() throws Exception {
- createDB();
- audioPlaybackStartupTime(MediaNames.MP3FILES);
- audioPlaybackStartupTime(MediaNames.AACFILES);
-
- //close the database after all transactions
- if (mDB.isOpen()) {
- mDB.close();
- }
- }
-
- public void wmametadatautility(String[] testFile) {
- long t1 = 0;
- long t2 = 0;
- long sum = 0;
- long duration = 0;
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- String value;
- for (int i = 0, n = testFile.length; i < n; ++i) {
- try {
- t1 = SystemClock.uptimeMillis();
- retriever.setDataSource(testFile[i]);
- value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
- value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
- value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER);
- value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE);
- value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
- value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR);
- value =
- retriever
- .extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER);
- t2 = SystemClock.uptimeMillis();
- duration = t2 - t1;
- Log.v(TAG, "Time taken = " + duration);
- sum = sum + duration;
- } catch (Exception e) {
- Log.v(TAG, e.getMessage());
- }
-
- }
- Log.v(TAG, "Average duration = " + sum / testFile.length);
- }
-
private void initializeMessageLooper() {
final ConditionVariable startDone = new ConditionVariable();
new Thread() {
@@ -421,13 +318,6 @@
return true;
}
- @Suppress
- public void testWmaParseTime() throws Exception {
- // createDB();
- wmametadatautility(MediaNames.WMASUPPORTED);
- }
-
-
// Test case 1: Capture the memory usage after every 20 h263 playback
@LargeTest
public void testH263VideoPlaybackMemoryUsage() throws Exception {
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
index 52bfc28..ed413e6 100644
--- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -60,6 +60,7 @@
IBackupManager mBackupManager;
FullObserver mObserver;
int mToken;
+ boolean mDidAcknowledge;
TextView mStatusView;
Button mAllowButton;
@@ -70,6 +71,7 @@
Context mContext;
ObserverHandler(Context context) {
mContext = context;
+ mDidAcknowledge = false;
}
@Override
@@ -157,11 +159,7 @@
mAllowButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- try {
- mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver);
- } catch (RemoteException e) {
- // TODO: bail gracefully if we can't contact the backup manager
- }
+ sendAcknowledgement(mToken, true, mObserver);
mAllowButton.setEnabled(false);
mDenyButton.setEnabled(false);
}
@@ -170,11 +168,7 @@
mDenyButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- try {
- mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, mObserver);
- } catch (RemoteException e) {
- // TODO: bail gracefully if we can't contact the backup manager
- }
+ sendAcknowledgement(mToken, false, mObserver);
mAllowButton.setEnabled(false);
mDenyButton.setEnabled(false);
}
@@ -187,14 +181,21 @@
// We explicitly equate departure from the UI with refusal. This includes the
// implicit configuration-changed stop/restart cycle.
- try {
- mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, null);
- } catch (RemoteException e) {
- // if this fails we'll still time out with no acknowledgment
- }
+ sendAcknowledgement(mToken, false, null);
finish();
}
+ void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver observer) {
+ if (!mDidAcknowledge) {
+ mDidAcknowledge = true;
+ try {
+ mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver);
+ } catch (RemoteException e) {
+ // TODO: bail gracefully if we can't contact the backup manager
+ }
+ }
+ }
+
/**
* The observer binder for showing backup/restore progress. This binder just bounces
* the notifications onto the main thread.
diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk
new file mode 100644
index 0000000..1d4f4da7
--- /dev/null
+++ b/packages/SharedStorageBackup/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_PACKAGE_NAME := SharedStorageBackup
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+########################
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/packages/SharedStorageBackup/AndroidManifest.xml b/packages/SharedStorageBackup/AndroidManifest.xml
new file mode 100644
index 0000000..258059c
--- /dev/null
+++ b/packages/SharedStorageBackup/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2011, 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedstoragebackup" >
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
+
+ <application android:allowClearUserData="false"
+ android:permission="android.permission.CONFIRM_FULL_BACKUP"
+ android:fullBackupAgent=".SharedStorageAgent"
+ android:allowBackup="false" >
+ </application>
+</manifest>
diff --git a/packages/SharedStorageBackup/proguard.flags b/packages/SharedStorageBackup/proguard.flags
new file mode 100644
index 0000000..f43cb81
--- /dev/null
+++ b/packages/SharedStorageBackup/proguard.flags
@@ -0,0 +1 @@
+-keep class com.android.sharedstoragebackup.SharedStorageAgent
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
new file mode 100644
index 0000000..b02ca2e
--- /dev/null
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
@@ -0,0 +1,93 @@
+package com.android.sharedstoragebackup;
+
+import android.app.backup.FullBackup;
+import android.app.backup.FullBackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.content.Context;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+public class SharedStorageAgent extends FullBackupAgent {
+ static final String TAG = "SharedStorageAgent";
+ static final boolean DEBUG = true;
+
+ StorageVolume[] mVolumes;
+
+ @Override
+ public void onCreate() {
+ StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
+ if (mgr != null) {
+ mVolumes = mgr.getVolumeList();
+ } else {
+ Slog.e(TAG, "Unable to access Storage Manager");
+ }
+ }
+
+ @Override
+ public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) throws IOException {
+ // If there are shared-storage volumes available, run the inherited directory-
+ // hierarchy backup process on them. By convention in the Storage Manager, the
+ // "primary" shared storage volume is first in the list.
+ if (mVolumes != null) {
+ for (int i = 0; i < mVolumes.length; i++) {
+ StorageVolume v = mVolumes[i];
+ // Express the contents of volume N this way in the tar stream:
+ // shared/N/path/to/file
+ // The restore will then extract to the given volume
+ String domain = FullBackup.SHARED_PREFIX + i;
+ processTree(null, domain, v.getPath(), null, data);
+ }
+ }
+ }
+
+ /**
+ * Incremental onRestore() implementation is not used.
+ */
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ }
+
+ /**
+ * Full restore of one file to shared storage
+ */
+ @Override
+ public void onRestoreFile(ParcelFileDescriptor data, long size,
+ int type, String domain, String relpath, long mode, long mtime)
+ throws IOException {
+ Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]");
+
+ File outFile = null;
+
+ // The file path must be in the semantic form [number]/path/to/file...
+ int slash = relpath.indexOf('/');
+ if (slash > 0) {
+ try {
+ int i = Integer.parseInt(relpath.substring(0, slash));
+ if (i <= mVolumes.length) {
+ outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1));
+ if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath());
+ } else {
+ Slog.w(TAG, "Cannot restore data for unavailable volume " + i);
+ }
+ } catch (NumberFormatException e) {
+ if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash));
+ }
+ } else {
+ if (DEBUG) Slog.i(TAG, "Can't find volume-number token");
+ }
+ if (outFile == null) {
+ Slog.e(TAG, "Skipping data with malformed path " + relpath);
+ }
+
+ FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, false);
+ }
+}
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..e2584e3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..58b8510
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..2795c34
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..bbed6a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
new file mode 100644
index 0000000..977e002
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_zoom_pressed" />
+ <item android:drawable="@drawable/ic_sysbar_zoom_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
index d9f3f23..707a8cb 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
@@ -75,6 +75,13 @@
systemui:keyCode="82"
android:visibility="invisible"
/>
+ <com.android.systemui.statusbar.policy.CompatModeButton
+ android:id="@+id/compat_button"
+ android:layout_width="80dip"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_sysbar_zoom"
+ android:visibility="invisible"
+ />
</LinearLayout>
<!-- fake space bar zone -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
new file mode 100644
index 0000000..9b44f78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class CompatModeButton extends ImageView implements View.OnClickListener {
+ private static final String TAG = "StatusBar.CompatModeButton";
+
+ private ActivityManager mAM;
+
+ public CompatModeButton(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CompatModeButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+
+ setClickable(true);
+
+ mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
+ setOnClickListener(this);
+
+ refresh();
+ }
+
+ @Override
+ public void onClick(View v) {
+ mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_TOGGLE);
+ }
+
+ public void refresh() {
+ setVisibility(
+ (mAM.getFrontActivityScreenCompatMode() == ActivityManager.COMPAT_MODE_NEVER)
+ ? View.GONE
+ : View.VISIBLE
+ );
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 70a78df..3175a99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -446,12 +446,14 @@
}
boolean isCdmaEri() {
- final int iconIndex = mServiceState.getCdmaEriIconIndex();
- if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
- final int iconMode = mServiceState.getCdmaEriIconMode();
- if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
- || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
- return true;
+ if (mServiceState != null) {
+ final int iconIndex = mServiceState.getCdmaEriIconIndex();
+ if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
+ final int iconMode = mServiceState.getCdmaEriIconMode();
+ if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
+ || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
+ return true;
+ }
}
}
return false;
@@ -854,7 +856,7 @@
pw.print(" mDataActivity=");
pw.println(mDataActivity);
pw.print(" mServiceState=");
- pw.println(mServiceState.toString());
+ pw.println(mServiceState);
pw.print(" mNetworkName=");
pw.println(mNetworkName);
pw.print(" mNetworkNameDefault=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 5b5801d..ffb45ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -69,6 +69,7 @@
import com.android.systemui.statusbar.*;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CompatModeButton;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.Prefs;
@@ -963,6 +964,10 @@
// See above re: lights-out policy for legacy apps.
if (visible) setLightsOn(true);
+
+ // XXX: HACK: not sure if this is the best way to catch a new activity that might require a
+ // change in compatibility features, but it's a start.
+ ((CompatModeButton) mBarContents.findViewById(R.id.compat_button)).refresh();
}
public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f806624..d4a8ddb 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -53,13 +53,6 @@
#include <media/EffectVisualizerApi.h>
// ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
-// ----------------------------------------------------------------------------
namespace android {
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index ef8d957..dd91788 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -39,13 +39,6 @@
#include <hardware/audio_policy.h>
#include <hardware/audio_policy_hal.h>
-// ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
namespace android {
static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n";
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
index f9fa30e..7a18831 100644
--- a/services/camera/libcameraservice/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -438,18 +438,23 @@
}
static void __data_cb(int32_t msg_type,
- const camera_memory_t *data,
+ const camera_memory_t *data, unsigned int index,
void *user)
{
LOGV("%s", __FUNCTION__);
CameraHardwareInterface *__this =
static_cast<CameraHardwareInterface *>(user);
sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
- __this->mDataCb(msg_type, mem, __this->mCbUser);
+ if (index >= mem->mNumBufs) {
+ LOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+ index, mem->mNumBufs);
+ return;
+ }
+ __this->mDataCb(msg_type, mem->mBuffers[index], __this->mCbUser);
}
static void __data_cb_timestamp(nsecs_t timestamp, int32_t msg_type,
- const camera_memory_t *data,
+ const camera_memory_t *data, unsigned index,
void *user)
{
LOGV("%s", __FUNCTION__);
@@ -459,38 +464,85 @@
// drop all references, it will be destroyed (as well as the enclosed
// MemoryHeapBase.
sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
- __this->mDataCbTimestamp(timestamp, msg_type, mem, __this->mCbUser);
+ if (index >= mem->mNumBufs) {
+ LOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+ index, mem->mNumBufs);
+ return;
+ }
+ __this->mDataCbTimestamp(timestamp, msg_type, mem->mBuffers[index], __this->mCbUser);
}
// This is a utility class that combines a MemoryHeapBase and a MemoryBase
// in one. Since we tend to use them in a one-to-one relationship, this is
// handy.
- class CameraHeapMemory : public MemoryBase {
+ class CameraHeapMemory : public RefBase {
public:
- CameraHeapMemory(size_t size) :
- MemoryBase(new MemoryHeapBase(size), 0, size)
+ CameraHeapMemory(int fd, size_t buf_size, uint_t num_buffers = 1) :
+ mBufSize(buf_size),
+ mNumBufs(num_buffers)
{
- handle.data = getHeap()->base();
- handle.size = size;
- handle.handle = this;
+ mHeap = new MemoryHeapBase(fd, buf_size * num_buffers);
+ commonInitialization();
}
+ CameraHeapMemory(size_t buf_size, uint_t num_buffers = 1) :
+ mBufSize(buf_size),
+ mNumBufs(num_buffers)
+ {
+ mHeap = new MemoryHeapBase(buf_size * num_buffers);
+ commonInitialization();
+ }
+
+ void commonInitialization()
+ {
+ handle.data = mHeap->base();
+ handle.size = mBufSize * mNumBufs;
+ handle.handle = this;
+
+ mBuffers = new sp<MemoryBase>[mNumBufs];
+ for (uint_t i = 0; i < mNumBufs; i++)
+ mBuffers[i] = new MemoryBase(mHeap,
+ i * mBufSize,
+ mBufSize);
+
+ handle.release = __put_memory;
+ }
+
+ virtual ~CameraHeapMemory()
+ {
+ delete [] mBuffers;
+ }
+
+ size_t mBufSize;
+ uint_t mNumBufs;
+ sp<MemoryHeapBase> mHeap;
+ sp<MemoryBase> *mBuffers;
+
camera_memory_t handle;
};
- static camera_memory_t* __get_memory(size_t size,
- void *user __attribute__((unused)))
+ static camera_memory_t* __get_memory(int fd, size_t buf_size, uint_t num_bufs,
+ void *user __attribute__((unused)))
{
- // We allocate the object here, but we do not assign it to a strong
- // pointer yet. The HAL will pass it back to us via the data callback
- // or the data-timestamp callback, and from there on we will wrap it
- // within a strong pointer.
-
- CameraHeapMemory *mem = new CameraHeapMemory(size);
+ CameraHeapMemory *mem;
+ if (fd < 0)
+ mem = new CameraHeapMemory(buf_size, num_bufs);
+ else
+ mem = new CameraHeapMemory(fd, buf_size, num_bufs);
+ mem->incStrong(mem);
return &mem->handle;
}
+ static void __put_memory(camera_memory_t *data)
+ {
+ if (!data)
+ return;
+
+ CameraHeapMemory *mem = static_cast<CameraHeapMemory *>(data->handle);
+ mem->decStrong(mem);
+ }
+
static ANativeWindow *__to_anw(void *user)
{
CameraHardwareInterface *__this =
@@ -541,7 +593,7 @@
static int __set_buffer_count(struct preview_stream_ops* w, int count)
{
ANativeWindow *a = anw(w);
- return native_window_set_buffer_count(a, count);
+ return native_window_set_buffer_count(a, count);
}
static int __set_buffers_geometry(struct preview_stream_ops* w,
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index c42e3ab..09b24a8 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -200,23 +200,6 @@
return edgeFlags;
}
-static void clampPositionUsingPointerBounds(
- const sp<PointerControllerInterface>& pointerController, float* x, float* y) {
- float minX, minY, maxX, maxY;
- if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
- if (*x < minX) {
- *x = minX;
- } else if (*x > maxX) {
- *x = maxX;
- }
- if (*y < minY) {
- *y = minY;
- } else if (*y > maxY) {
- *y = maxY;
- }
- }
-}
-
static float calculateCommonVector(float a, float b) {
if (a > 0 && b > 0) {
return a < b ? a : b;
@@ -787,8 +770,8 @@
mConfig.pointerGestureTapSlop);
dump.appendFormat(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
- dump.appendFormat(INDENT3 "MultitouchMinSpeed: %0.1fpx/s\n",
- mConfig.pointerGestureMultitouchMinSpeed);
+ dump.appendFormat(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
+ mConfig.pointerGestureMultitouchMinDistance);
dump.appendFormat(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
mConfig.pointerGestureSwipeTransitionAngleCosine);
dump.appendFormat(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
@@ -3509,11 +3492,18 @@
cancelPreviousGesture = false;
}
- // Switch pointer presentation.
- mPointerController->setPresentation(
- mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
- ? PointerControllerInterface::PRESENTATION_SPOT
- : PointerControllerInterface::PRESENTATION_POINTER);
+ // Update the pointer presentation and spots.
+ if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+ mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
+ if (finishPreviousGesture || cancelPreviousGesture) {
+ mPointerController->clearSpots();
+ }
+ mPointerController->setSpots(mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.currentGestureIdBits);
+ } else {
+ mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+ }
// Show or hide the pointer if needed.
switch (mPointerGesture.currentGestureMode) {
@@ -3712,12 +3702,6 @@
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.pointerVelocityControl.reset();
-
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
- mPointerGesture.spotIdBits.clear();
- moveSpotsLocked();
- }
return true;
}
}
@@ -3798,22 +3782,18 @@
if (isQuietTime) {
// Case 1: Quiet time. (QUIET)
#if DEBUG_GESTURES
- LOGD("Gestures: QUIET for next %0.3fms",
- (mPointerGesture.quietTime + QUIET_INTERVAL - when) * 0.000001f);
+ LOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime
+ + mConfig->pointerGestureQuietInterval - when) * 0.000001f);
#endif
- *outFinishPreviousGesture = true;
+ if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
+ *outFinishPreviousGesture = true;
+ }
mPointerGesture.activeGestureId = -1;
mPointerGesture.currentGestureMode = PointerGesture::QUIET;
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.pointerVelocityControl.reset();
-
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
- mPointerGesture.spotIdBits.clear();
- moveSpotsLocked();
- }
} else if (isPointerDown(mCurrentTouch.buttonState)) {
// Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
// The pointer follows the active touch point.
@@ -3899,32 +3879,11 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- if (activeTouchId >= 0) {
- // Collapse all spots into one point at the pointer location.
- mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_DRAG;
- mPointerGesture.spotIdBits.clear();
- for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
- uint32_t id = mCurrentTouch.pointers[i].id;
- mPointerGesture.spotIdBits.markBit(id);
- mPointerGesture.spotIdToIndex[id] = i;
- mPointerGesture.spotCoords[i] = mPointerGesture.currentGestureCoords[0];
- }
- } else {
- // No fingers. Generate a spot at the pointer location so the
- // anchor appears to be pressed.
- mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_CLICK;
- mPointerGesture.spotIdBits.clear();
- mPointerGesture.spotIdBits.markBit(0);
- mPointerGesture.spotIdToIndex[0] = 0;
- mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
- }
- moveSpotsLocked();
- }
} else if (mCurrentTouch.pointerCount == 0) {
// Case 3. No fingers down and button is not pressed. (NEUTRAL)
- *outFinishPreviousGesture = true;
+ if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
+ *outFinishPreviousGesture = true;
+ }
// Watch for taps coming out of HOVER or TAP_DRAG mode.
// Checking for taps after TAP_DRAG allows us to detect double-taps.
@@ -3965,15 +3924,6 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(
AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_TAP;
- mPointerGesture.spotIdBits.clear();
- mPointerGesture.spotIdBits.markBit(lastActiveTouchId);
- mPointerGesture.spotIdToIndex[lastActiveTouchId] = 0;
- mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
- moveSpotsLocked();
- }
-
tapped = true;
} else {
#if DEBUG_GESTURES
@@ -3999,12 +3949,6 @@
mPointerGesture.activeGestureId = -1;
mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
mPointerGesture.currentGestureIdBits.clear();
-
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
- mPointerGesture.spotIdBits.clear();
- moveSpotsLocked();
- }
}
} else if (mCurrentTouch.pointerCount == 1) {
// Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
@@ -4067,7 +4011,9 @@
#if DEBUG_GESTURES
LOGD("Gestures: HOVER");
#endif
- *outFinishPreviousGesture = true;
+ if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
+ *outFinishPreviousGesture = true;
+ }
mPointerGesture.activeGestureId = 0;
down = false;
}
@@ -4094,16 +4040,6 @@
mPointerGesture.tapX = x;
mPointerGesture.tapY = y;
}
-
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerGesture.spotGesture = down ? PointerControllerInterface::SPOT_GESTURE_DRAG
- : PointerControllerInterface::SPOT_GESTURE_HOVER;
- mPointerGesture.spotIdBits.clear();
- mPointerGesture.spotIdBits.markBit(activeTouchId);
- mPointerGesture.spotIdToIndex[activeTouchId] = 0;
- mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
- moveSpotsLocked();
- }
} else {
// Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
// We need to provide feedback for each finger that goes down so we cannot wait
@@ -4131,8 +4067,8 @@
// Reset the gesture.
#if DEBUG_GESTURES
LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
- "settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
+ "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
+ + mConfig->pointerGestureMultitouchSettleInterval - when)
* 0.000001f);
#endif
*outCancelPreviousGesture = true;
@@ -4147,101 +4083,134 @@
mPointerGesture.referenceIdBits.clear();
mPointerGesture.pointerVelocityControl.reset();
- if (settled && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
- && mLastTouch.idBits.hasBit(mPointerGesture.activeTouchId)) {
- // The spot is already visible and has settled, use it as the reference point
- // for the gesture. Other spots will be positioned relative to this one.
+ // Use the centroid and pointer location as the reference points for the gesture.
#if DEBUG_GESTURES
- LOGD("Gestures: Using active spot as reference for MULTITOUCH, "
- "settle time expired %0.3fms ago",
- (when - mPointerGesture.firstTouchTime - MULTITOUCH_SETTLE_INTERVAL)
- * 0.000001f);
+ LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
+ "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
+ + mConfig->pointerGestureMultitouchSettleInterval - when)
+ * 0.000001f);
#endif
- const PointerData& d = mLastTouch.pointers[mLastTouch.idToIndex[
- mPointerGesture.activeTouchId]];
- mPointerGesture.referenceTouchX = d.x;
- mPointerGesture.referenceTouchY = d.y;
- const PointerCoords& c = mPointerGesture.spotCoords[mPointerGesture.spotIdToIndex[
- mPointerGesture.activeTouchId]];
- mPointerGesture.referenceGestureX = c.getAxisValue(AMOTION_EVENT_AXIS_X);
- mPointerGesture.referenceGestureY = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
+ mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
+ &mPointerGesture.referenceTouchY);
+ mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+ &mPointerGesture.referenceGestureY);
+ }
+
+ // Clear the reference deltas for fingers not yet included in the reference calculation.
+ for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value);
+ !idBits.isEmpty(); ) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+
+ mPointerGesture.referenceDeltas[id].dx = 0;
+ mPointerGesture.referenceDeltas[id].dy = 0;
+ }
+ mPointerGesture.referenceIdBits = mCurrentTouch.idBits;
+
+ // Add delta for all fingers and calculate a common movement delta.
+ float commonDeltaX = 0, commonDeltaY = 0;
+ BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
+ for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
+ bool first = (idBits == commonIdBits);
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+
+ const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
+ const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ delta.dx += cpd.x - lpd.x;
+ delta.dy += cpd.y - lpd.y;
+
+ if (first) {
+ commonDeltaX = delta.dx;
+ commonDeltaY = delta.dy;
} else {
- // Use the centroid and pointer location as the reference points for the gesture.
-#if DEBUG_GESTURES
- LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
- "settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
- * 0.000001f);
-#endif
- mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
- &mPointerGesture.referenceTouchY);
- mPointerController->getPosition(&mPointerGesture.referenceGestureX,
- &mPointerGesture.referenceGestureY);
+ commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
+ commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
}
}
+ // Consider transitions from PRESS to SWIPE or MULTITOUCH.
if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
- float d;
- if (mCurrentTouch.pointerCount > 2) {
- // There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
- LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
- mCurrentTouch.pointerCount);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- } else if (((d = distance(
- mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
- mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
- > mLocked.pointerGestureMaxSwipeWidth)) {
- // There are two pointers but they are too far apart, switch to FREEFORM.
-#if DEBUG_GESTURES
- LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
- d, mLocked.pointerGestureMaxSwipeWidth);
-#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
- } else {
- // There are two pointers. Wait for both pointers to start moving
- // before deciding whether this is a SWIPE or FREEFORM gesture.
- uint32_t id1 = mCurrentTouch.pointers[0].id;
- uint32_t id2 = mCurrentTouch.pointers[1].id;
+ float dist[MAX_POINTER_ID + 1];
+ int32_t distOverThreshold = 0;
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
- float vx1, vy1, vx2, vy2;
- mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1);
- mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2);
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ dist[id] = hypotf(delta.dx * mLocked.pointerGestureXZoomScale,
+ delta.dy * mLocked.pointerGestureYZoomScale);
+ if (dist[id] > mConfig->pointerGestureMultitouchMinDistance) {
+ distOverThreshold += 1;
+ }
+ }
- float speed1 = hypotf(vx1, vy1);
- float speed2 = hypotf(vx2, vy2);
- if (speed1 >= mConfig->pointerGestureMultitouchMinSpeed
- && speed2 >= mConfig->pointerGestureMultitouchMinSpeed) {
- // Calculate the dot product of the velocity vectors.
- // When the vectors are oriented in approximately the same direction,
- // the angle betweeen them is near zero and the cosine of the angle
- // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
- float dot = vx1 * vx2 + vy1 * vy2;
- float cosine = dot / (speed1 * speed2); // denominator always > 0
- if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
- // Pointers are moving in the same direction. Switch to SWIPE.
+ // Only transition when at least two pointers have moved further than
+ // the minimum distance threshold.
+ if (distOverThreshold >= 2) {
+ float d;
+ if (mCurrentTouch.pointerCount > 2) {
+ // There are more than two pointers, switch to FREEFORM.
#if DEBUG_GESTURES
- LOGD("Gestures: PRESS transitioned to SWIPE, "
- "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
- "cosine %0.3f >= %0.3f",
- speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
- cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+ LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+ mCurrentTouch.pointerCount);
#endif
- mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
- } else {
- // Pointers are moving in different directions. Switch to FREEFORM.
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ } else if (((d = distance(
+ mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
+ mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
+ > mLocked.pointerGestureMaxSwipeWidth)) {
+ // There are two pointers but they are too far apart for a SWIPE,
+ // switch to FREEFORM.
#if DEBUG_GESTURES
- LOGD("Gestures: PRESS transitioned to FREEFORM, "
- "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
- "cosine %0.3f < %0.3f",
- speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
- cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+ LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+ d, mLocked.pointerGestureMaxSwipeWidth);
#endif
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ } else {
+ // There are two pointers. Wait for both pointers to start moving
+ // before deciding whether this is a SWIPE or FREEFORM gesture.
+ uint32_t id1 = mCurrentTouch.pointers[0].id;
+ uint32_t id2 = mCurrentTouch.pointers[1].id;
+ float dist1 = dist[id1];
+ float dist2 = dist[id2];
+ if (dist1 >= mConfig->pointerGestureMultitouchMinDistance
+ && dist2 >= mConfig->pointerGestureMultitouchMinDistance) {
+ // Calculate the dot product of the displacement vectors.
+ // When the vectors are oriented in approximately the same direction,
+ // the angle betweeen them is near zero and the cosine of the angle
+ // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
+ PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
+ PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
+ float dot = delta1.dx * delta2.dx + delta1.dy * delta2.dy;
+ float cosine = dot / (dist1 * dist2); // denominator always > 0
+ if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
+ // Pointers are moving in the same direction. Switch to SWIPE.
+#if DEBUG_GESTURES
+ LOGD("Gestures: PRESS transitioned to SWIPE, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f >= %0.3f",
+ dist1, mConfig->pointerGestureMultitouchMinDistance,
+ dist2, mConfig->pointerGestureMultitouchMinDistance,
+ cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+#endif
+ mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+ } else {
+ // Pointers are moving in different directions. Switch to FREEFORM.
+#if DEBUG_GESTURES
+ LOGD("Gestures: PRESS transitioned to FREEFORM, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f < %0.3f",
+ dist1, mConfig->pointerGestureMultitouchMinDistance,
+ dist2, mConfig->pointerGestureMultitouchMinDistance,
+ cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+#endif
+ *outCancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+ }
}
}
}
@@ -4258,67 +4227,28 @@
}
}
- // Clear the reference deltas for fingers not yet included in the reference calculation.
- for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value);
- !idBits.isEmpty(); ) {
- uint32_t id = idBits.firstMarkedBit();
- idBits.clearBit(id);
-
- mPointerGesture.referenceDeltas[id].dx = 0;
- mPointerGesture.referenceDeltas[id].dy = 0;
- }
- mPointerGesture.referenceIdBits = mCurrentTouch.idBits;
-
- // Move the reference points based on the overall group motion of the fingers.
- // The objective is to calculate a vector delta that is common to the movement
- // of all fingers.
- BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
- if (!commonIdBits.isEmpty()) {
- float commonDeltaX = 0, commonDeltaY = 0;
- for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
- bool first = (idBits == commonIdBits);
+ // Move the reference points based on the overall group motion of the fingers
+ // except in PRESS mode while waiting for a transition to occur.
+ if (mPointerGesture.currentGestureMode != PointerGesture::PRESS
+ && (commonDeltaX || commonDeltaY)) {
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
- const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
- const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx += cpd.x - lpd.x;
- delta.dy += cpd.y - lpd.y;
-
- if (first) {
- commonDeltaX = delta.dx;
- commonDeltaY = delta.dy;
- } else {
- commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
- commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
- }
+ delta.dx = 0;
+ delta.dy = 0;
}
- if (commonDeltaX || commonDeltaY) {
- for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.firstMarkedBit();
- idBits.clearBit(id);
+ mPointerGesture.referenceTouchX += commonDeltaX;
+ mPointerGesture.referenceTouchY += commonDeltaY;
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx = 0;
- delta.dy = 0;
- }
+ commonDeltaX *= mLocked.pointerGestureXMovementScale;
+ commonDeltaY *= mLocked.pointerGestureYMovementScale;
+ mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
- mPointerGesture.referenceTouchX += commonDeltaX;
- mPointerGesture.referenceTouchY += commonDeltaY;
-
- commonDeltaX *= mLocked.pointerGestureXMovementScale;
- commonDeltaY *= mLocked.pointerGestureYMovementScale;
- mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
-
- mPointerGesture.referenceGestureX += commonDeltaX;
- mPointerGesture.referenceGestureY += commonDeltaY;
-
- clampPositionUsingPointerBounds(mPointerController,
- &mPointerGesture.referenceGestureX,
- &mPointerGesture.referenceGestureY);
- }
+ mPointerGesture.referenceGestureX += commonDeltaX;
+ mPointerGesture.referenceGestureY += commonDeltaY;
}
// Report gestures.
@@ -4344,10 +4274,6 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
mPointerGesture.referenceGestureY);
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_PRESS;
- }
} else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
// SWIPE mode.
#if DEBUG_GESTURES
@@ -4370,10 +4296,6 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
mPointerGesture.referenceGestureY);
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_SWIPE;
- }
} else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
// FREEFORM mode.
#if DEBUG_GESTURES
@@ -4475,33 +4397,6 @@
"activeGestureId=%d", mPointerGesture.activeGestureId);
#endif
}
-
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_FREEFORM;
- }
- }
-
- // Update spot locations for PRESS, SWIPE and FREEFORM.
- // We use the same calculation as we do to calculate the gesture pointers
- // for FREEFORM so that the spots smoothly track gestures.
- if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
- mPointerGesture.spotIdBits.clear();
- for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
- uint32_t id = mCurrentTouch.pointers[i].id;
- mPointerGesture.spotIdBits.markBit(id);
- mPointerGesture.spotIdToIndex[id] = i;
-
- float x = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX)
- * mLocked.pointerGestureXZoomScale + mPointerGesture.referenceGestureX;
- float y = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY)
- * mLocked.pointerGestureYZoomScale + mPointerGesture.referenceGestureY;
-
- mPointerGesture.spotCoords[i].clear();
- mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- }
- moveSpotsLocked();
}
}
@@ -4544,11 +4439,6 @@
return true;
}
-void TouchInputMapper::moveSpotsLocked() {
- mPointerController->setSpots(mPointerGesture.spotGesture,
- mPointerGesture.spotCoords, mPointerGesture.spotIdToIndex, mPointerGesture.spotIdBits);
-}
-
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
const PointerProperties* properties, const PointerCoords* coords,
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 1d4ad87..36cd89c 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -101,8 +101,8 @@
nsecs_t pointerGestureMultitouchSettleInterval;
// The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
- // both of the pointers are moving at least this fast.
- float pointerGestureMultitouchMinSpeed; // in pixels per second
+ // at least two pointers have moved at least this far from their starting place.
+ float pointerGestureMultitouchMinDistance; // in pixels
// The transition from PRESS to SWIPE gesture mode can only occur when the
// cosine of the angle between the two vectors is greater than or equal to than this value
@@ -134,7 +134,7 @@
filterTouchEvents(false),
filterJumpyTouchEvents(false),
virtualKeyQuietTime(0),
- pointerVelocityControlParameters(1.0f, 80.0f, 400.0f, 4.0f),
+ pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
@@ -142,10 +142,10 @@
pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
pointerGestureTapSlop(10.0f), // 10 pixels
pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
- pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second
+ pointerGestureMultitouchMinDistance(15), // 15 pixels
pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees
- pointerGestureSwipeMaxWidthRatio(0.333f),
- pointerGestureMovementSpeedRatio(0.3f),
+ pointerGestureSwipeMaxWidthRatio(0.25f),
+ pointerGestureMovementSpeedRatio(0.8f),
pointerGestureZoomSpeedRatio(0.3f) { }
};
@@ -1140,12 +1140,6 @@
PointerProperties lastGestureProperties[MAX_POINTERS];
PointerCoords lastGestureCoords[MAX_POINTERS];
- // Pointer coords and ids for the current spots.
- PointerControllerInterface::SpotGesture spotGesture;
- BitSet32 spotIdBits; // same set of ids as touch ids
- uint32_t spotIdToIndex[MAX_POINTER_ID + 1];
- PointerCoords spotCoords[MAX_POINTERS];
-
// Time the pointer gesture last went down.
nsecs_t downTime;
@@ -1192,8 +1186,6 @@
currentGestureIdBits.clear();
lastGestureMode = NEUTRAL;
lastGestureIdBits.clear();
- spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
- spotIdBits.clear();
downTime = 0;
velocityTracker.clear();
resetTap();
@@ -1219,7 +1211,6 @@
void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
bool preparePointerGestures(nsecs_t when,
bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout);
- void moveSpotsLocked();
// Dispatches a motion event.
// If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index c18ebcf..12c7cba 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -240,15 +240,15 @@
}
}
-void PointerController::setSpots(SpotGesture spotGesture,
- const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+void PointerController::setSpots(const PointerCoords* spotCoords,
+ const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
#if DEBUG_POINTER_UPDATES
- LOGD("setSpots: spotGesture=%d", spotGesture);
+ LOGD("setSpots: idBits=%08x", spotIdBits.value);
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
uint32_t id = idBits.firstMarkedBit();
idBits.clearBit(id);
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
- LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
+ LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
c.getAxisValue(AMOTION_EVENT_AXIS_X),
c.getAxisValue(AMOTION_EVENT_AXIS_Y),
c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index 1c21db1..700ef72 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -90,38 +90,6 @@
/* Sets the mode of the pointer controller. */
virtual void setPresentation(Presentation presentation) = 0;
- // Describes the current gesture.
- enum SpotGesture {
- // No gesture.
- // Do not display any spots.
- SPOT_GESTURE_NEUTRAL,
- // Tap at current location.
- // Briefly display one spot at the tapped location.
- SPOT_GESTURE_TAP,
- // Drag at current location.
- // Display spot at pressed location.
- SPOT_GESTURE_DRAG,
- // Button pressed but no finger is down.
- // Display spot at pressed location.
- SPOT_GESTURE_BUTTON_CLICK,
- // Button pressed and a finger is down.
- // Display spot at pressed location.
- SPOT_GESTURE_BUTTON_DRAG,
- // One finger down and hovering.
- // Display spot at the hovered location.
- SPOT_GESTURE_HOVER,
- // Two fingers down but not sure in which direction they are moving so we consider
- // it a press at the pointer location.
- // Display two spots near the pointer location.
- SPOT_GESTURE_PRESS,
- // Two fingers down and moving in same direction.
- // Display two spots near the pointer location.
- SPOT_GESTURE_SWIPE,
- // Two or more fingers down and moving in arbitrary directions.
- // Display two or more spots near the pointer location, one for each finger.
- SPOT_GESTURE_FREEFORM,
- };
-
/* Sets the spots for the current gesture.
* The spots are not subject to the inactivity timeout like the pointer
* itself it since they are expected to remain visible for so long as
@@ -131,8 +99,7 @@
* For spotCoords, pressure != 0 indicates that the spot's location is being
* pressed (not hovering).
*/
- virtual void setSpots(SpotGesture spotGesture,
- const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+ virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits) = 0;
/* Removes all spots. */
@@ -198,8 +165,8 @@
virtual void unfade(Transition transition);
virtual void setPresentation(Presentation presentation);
- virtual void setSpots(SpotGesture spotGesture,
- const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
+ virtual void setSpots(const PointerCoords* spotCoords,
+ const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
virtual void clearSpots();
void setDisplaySize(int32_t width, int32_t height);
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 00b4222..d04c9e7 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -101,8 +101,8 @@
virtual void setPresentation(Presentation presentation) {
}
- virtual void setSpots(SpotGesture spotGesture,
- const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+ virtual void setSpots(const PointerCoords* spotCoords,
+ const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
}
virtual void clearSpots() {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 7c6d3c1..c28732e 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -134,6 +134,7 @@
// Timeout intervals for agent backup & restore operations
static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
+ static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
// User confirmation timeout for a full backup/restore operation
@@ -1691,7 +1692,7 @@
public void run() {
final List<PackageInfo> packagesToBackup;
- Slog.i(TAG, "--- Performing full-dataset restore ---");
+ Slog.i(TAG, "--- Performing full-dataset backup ---");
sendStartBackup();
// doAllApps supersedes the package set if any
@@ -1720,64 +1721,23 @@
}
}
- // Now back up the app data via the agent mechanism
PackageInfo pkg = null;
try {
+ // Now back up the app data via the agent mechanism
int N = packagesToBackup.size();
for (int i = 0; i < N; i++) {
pkg = packagesToBackup.get(i);
+ backupOnePackage(pkg);
+ }
- Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
-
- IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
- IApplicationThread.BACKUP_MODE_FULL);
- if (agent != null) {
- try {
- ApplicationInfo app = pkg.applicationInfo;
- boolean sendApk = mIncludeApks
- && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
- && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
- (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
-
- sendOnBackupPackage(pkg.packageName);
-
- {
- BackupDataOutput output = new BackupDataOutput(
- mOutputFile.getFileDescriptor());
-
- if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
- writeAppManifest(pkg, mManifestFile, sendApk);
- FullBackup.backupToTar(pkg.packageName, null, null,
- mFilesDir.getAbsolutePath(),
- mManifestFile.getAbsolutePath(),
- output);
- }
-
- if (DEBUG) Slog.d(TAG, "Calling doBackup()");
- final int token = generateToken();
- prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
- agent.doBackup(null, mOutputFile, null, sendApk,
- token, mBackupManagerBinder);
- boolean success = waitUntilOperationComplete(token);
- if (!success) {
- Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
- } else {
- if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
- }
- } catch (IOException e) {
- Slog.e(TAG, "Error backing up " + pkg.packageName, e);
- }
- } else {
- Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
- }
- tearDown(pkg);
+ // Finally, shared storage if requested
+ if (mIncludeShared) {
+ backupSharedStorage();
}
} catch (RemoteException e) {
Slog.e(TAG, "App died during full backup");
} finally {
- if (pkg != null) {
- tearDown(pkg);
- }
+ tearDown(pkg);
try {
mOutputFile.close();
} catch (IOException e) {
@@ -1796,6 +1756,79 @@
}
}
+ private void backupOnePackage(PackageInfo pkg) throws RemoteException {
+ Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
+
+ IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+ IApplicationThread.BACKUP_MODE_FULL);
+ if (agent != null) {
+ try {
+ ApplicationInfo app = pkg.applicationInfo;
+ boolean sendApk = mIncludeApks
+ && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
+ && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+ (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+
+ sendOnBackupPackage(pkg.packageName);
+
+ {
+ BackupDataOutput output = new BackupDataOutput(
+ mOutputFile.getFileDescriptor());
+
+ if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
+ writeAppManifest(pkg, mManifestFile, sendApk);
+ FullBackup.backupToTar(pkg.packageName, null, null,
+ mFilesDir.getAbsolutePath(),
+ mManifestFile.getAbsolutePath(),
+ output);
+ }
+
+ if (DEBUG) Slog.d(TAG, "Calling doBackup()");
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
+ agent.doBackup(null, mOutputFile, null, sendApk,
+ token, mBackupManagerBinder);
+ if (!waitUntilOperationComplete(token)) {
+ Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Error backing up " + pkg.packageName, e);
+ }
+ } else {
+ Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
+ }
+ tearDown(pkg);
+ }
+
+ private void backupSharedStorage() throws RemoteException {
+ PackageInfo pkg = null;
+ try {
+ pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0);
+ IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+ IApplicationThread.BACKUP_MODE_FULL);
+ if (agent != null) {
+ sendOnBackupPackage("Shared storage");
+
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
+ agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder);
+ if (!waitUntilOperationComplete(token)) {
+ Slog.e(TAG, "Full backup failed on shared storage");
+ } else {
+ if (DEBUG) Slog.d(TAG, "Full shared storage backup success");
+ }
+ } else {
+ Slog.e(TAG, "Could not bind to shared storage backup agent");
+ }
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Shared storage backup package not found");
+ } finally {
+ tearDown(pkg);
+ }
+ }
+
private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
throws IOException {
// Manifest format. All data are strings ending in LF:
@@ -1836,23 +1869,24 @@
}
private void tearDown(PackageInfo pkg) {
- final ApplicationInfo app = pkg.applicationInfo;
- try {
- // unbind and tidy up even on timeout or failure, just in case
- mActivityManager.unbindBackupAgent(app);
+ if (pkg != null) {
+ final ApplicationInfo app = pkg.applicationInfo;
+ if (app != null) {
+ try {
+ // unbind and tidy up even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(app);
- // The agent was running with a stub Application object, so shut it down.
- // !!! We hardcode the confirmation UI's package name here rather than use a
- // manifest flag! TODO something less direct.
- if (app.uid != Process.SYSTEM_UID
- && !pkg.packageName.equals("com.android.backupconfirm")) {
- if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
- mActivityManager.killApplicationProcess(app.processName, app.uid);
- } else {
- if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+ // The agent was running with a stub Application object, so shut it down.
+ if (app.uid != Process.SYSTEM_UID) {
+ if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Lost app trying to shut down");
+ }
}
- } catch (RemoteException e) {
- Slog.d(TAG, "Lost app trying to shut down");
}
}
@@ -1949,6 +1983,7 @@
// with a whitelist of packages known to be unclearable.
mClearedPackages.add("android");
mClearedPackages.add("com.android.providers.settings");
+
}
class RestoreFileRunnable implements Runnable {
@@ -1988,6 +2023,11 @@
Slog.i(TAG, "--- Performing full-dataset restore ---");
sendStartRestore();
+ // Are we able to restore shared-storage data?
+ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
+ }
+
try {
byte[] buffer = new byte[32 * 1024];
FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
@@ -2707,7 +2747,9 @@
info.path, 0, FullBackup.SHARED_PREFIX.length())) {
// File in shared storage. !!! TODO: implement this.
info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
+ info.packageName = "com.android.sharedstoragebackup";
info.domain = FullBackup.SHARED_STORAGE_TOKEN;
+ if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
} else if (FullBackup.APPS_PREFIX.regionMatches(0,
info.path, 0, FullBackup.APPS_PREFIX.length())) {
// App content! Parse out the package name and domain
@@ -2817,6 +2859,7 @@
final int end = offset + maxChars;
for (int i = offset; i < end; i++) {
final byte b = data[i];
+ // Numeric fields in tar can terminate with either NUL or SPC
if (b == 0 || b == ' ') break;
if (b < '0' || b > ('0' + radix - 1)) {
throw new IOException("Invalid number in header");
@@ -2829,8 +2872,8 @@
String extractString(byte[] data, int offset, int maxChars) throws IOException {
final int end = offset + maxChars;
int eos = offset;
- // tar string fields can end with either NUL or SPC
- while (eos < end && data[eos] != 0 && data[eos] != ' ') eos++;
+ // tar string fields terminate early with a NUL
+ while (eos < end && data[eos] != 0) eos++;
return new String(data, offset, eos-offset, "US-ASCII");
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index dd76eb8..e3d4c45 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
@@ -38,6 +39,7 @@
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkState;
import android.net.NetworkStateTracker;
import android.net.NetworkUtils;
import android.net.Proxy;
@@ -65,6 +67,7 @@
import com.android.internal.telephony.Phone;
import com.android.server.connectivity.Tethering;
+import com.google.android.collect.Lists;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -458,6 +461,12 @@
mTethering.getTetherableBluetoothRegexs().length != 0) &&
mTethering.getUpstreamIfaceRegexs().length != 0);
+ try {
+ nmService.registerObserver(mTethering);
+ } catch (RemoteException e) {
+ loge("Error registering observer :" + e);
+ }
+
if (DBG) {
mInetLog = new ArrayList();
}
@@ -557,25 +566,32 @@
*/
private boolean isNetworkBlocked(NetworkInfo info, int uid) {
synchronized (mUidRules) {
- return isNetworkBlockedLocked(info, uid);
+ // TODO: expand definition of "paid" network to cover tethered or
+ // paid hotspot use cases.
+ final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
+ final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+
+ if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
+ return true;
+ }
+
+ // no restrictive rules; network is visible
+ return false;
}
}
/**
- * Check if UID is blocked from using the given {@link NetworkInfo}.
+ * Return a filtered version of the given {@link NetworkInfo}, potentially
+ * marked {@link DetailedState#BLOCKED} based on
+ * {@link #isNetworkBlocked(NetworkInfo, int)}.
*/
- private boolean isNetworkBlockedLocked(NetworkInfo info, int uid) {
- // TODO: expand definition of "paid" network to cover tethered or paid
- // hotspot use cases.
- final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
- final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
-
- if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
- return true;
+ private NetworkInfo filterNetworkInfo(NetworkInfo info, int uid) {
+ if (isNetworkBlocked(info, uid)) {
+ // network is blocked; clone and override state
+ info = new NetworkInfo(info);
+ info.setDetailedState(DetailedState.BLOCKED, null, null);
}
-
- // no restrictive rules; network is visible
- return false;
+ return info;
}
/**
@@ -610,12 +626,7 @@
if (isNetworkTypeValid(networkType)) {
final NetworkStateTracker tracker = mNetTrackers[networkType];
if (tracker != null) {
- info = tracker.getNetworkInfo();
- if (isNetworkBlocked(info, uid)) {
- // network is blocked; clone and override state
- info = new NetworkInfo(info);
- info.setDetailedState(DetailedState.BLOCKED, null, null);
- }
+ info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
}
}
return info;
@@ -625,22 +636,15 @@
public NetworkInfo[] getAllNetworkInfo() {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
- final NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
- int i = 0;
+ final ArrayList<NetworkInfo> result = Lists.newArrayList();
synchronized (mUidRules) {
for (NetworkStateTracker tracker : mNetTrackers) {
if (tracker != null) {
- NetworkInfo info = tracker.getNetworkInfo();
- if (isNetworkBlockedLocked(info, uid)) {
- // network is blocked; clone and override state
- info = new NetworkInfo(info);
- info.setDetailedState(DetailedState.BLOCKED, null, null);
- }
- result[i++] = info;
+ result.add(filterNetworkInfo(tracker.getNetworkInfo(), uid));
}
}
}
- return result;
+ return result.toArray(new NetworkInfo[result.size()]);
}
/**
@@ -668,6 +672,23 @@
return null;
}
+ @Override
+ public NetworkState[] getAllNetworkState() {
+ enforceAccessPermission();
+ final int uid = Binder.getCallingUid();
+ final ArrayList<NetworkState> result = Lists.newArrayList();
+ synchronized (mUidRules) {
+ for (NetworkStateTracker tracker : mNetTrackers) {
+ if (tracker != null) {
+ final NetworkInfo info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
+ result.add(new NetworkState(
+ info, tracker.getLinkProperties(), tracker.getLinkCapabilities()));
+ }
+ }
+ }
+ return result.toArray(new NetworkState[result.size()]);
+ }
+
public boolean setRadios(boolean turnOn) {
boolean result = true;
enforceChangePermission();
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index f78dca9..c86f962 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1121,16 +1121,20 @@
com.android.internal.R.styleable.Storage_emulated, false);
int mtpReserve = a.getInt(
com.android.internal.R.styleable.Storage_mtpReserve, 0);
+ boolean allowMassStorage = a.getBoolean(
+ com.android.internal.R.styleable.Storage_allowMassStorage, false);
Slog.d(TAG, "got storage path: " + path + " description: " + description +
" primary: " + primary + " removable: " + removable +
- " emulated: " + emulated + " mtpReserve: " + mtpReserve);
+ " emulated: " + emulated + " mtpReserve: " + mtpReserve +
+ " allowMassStorage: " + allowMassStorage);
if (path == null || description == null) {
Slog.e(TAG, "path or description is null in readStorageList");
} else {
String pathString = path.toString();
StorageVolume volume = new StorageVolume(pathString,
- description.toString(), removable, emulated, mtpReserve);
+ description.toString(), removable, emulated,
+ mtpReserve, allowMassStorage);
if (primary) {
if (mPrimaryVolume == null) {
mPrimaryVolume = volume;
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 99c8af6..08c6699 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -138,7 +138,7 @@
if (outFile == null) {
Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]");
}
- FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+ FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
if (restoredWallpaper) {
WallpaperManagerService wallpaper =
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 596cbac..fd03e5f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -305,6 +305,7 @@
Slog.i(TAG, "Connectivity Service");
connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
+ networkStats.bindConnectivityManager(connectivity);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Connectivity Service", e);
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 41ec63a..4e2501b 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -321,6 +321,13 @@
}
break;
}
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1);
+ mWifiStateMachineChannel = null;
+ //Re-establish connection to state machine
+ mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
+ break;
+ }
default: {
Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
break;
@@ -593,7 +600,12 @@
*/
public WifiConfiguration getWifiApConfiguration() {
enforceAccessPermission();
- return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel);
+ if (mWifiStateMachineChannel != null) {
+ return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel);
+ } else {
+ Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+ return null;
+ }
}
/**
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 6ef6963..6c7895e 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -36,16 +36,21 @@
import android.text.TextUtils;
import android.util.Slog;
+import java.io.BufferedInputStream;
+import java.io.InputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
+import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
+import java.net.URL;
import java.util.Collection;
import java.util.List;
import java.util.Random;
+import java.util.Scanner;
/**
* {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi
@@ -195,6 +200,34 @@
return Settings.Secure.getInt(mContentResolver,
Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS, 250);
}
+
+ /**
+ * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED
+ */
+ private Boolean isWalledGardenTestEnabled() {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1;
+ }
+
+ /**
+ * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL
+ */
+ private String getWalledGardenUrl() {
+ String url = Settings.Secure.getString(mContentResolver,
+ Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL);
+ if (TextUtils.isEmpty(url)) return "http://www.google.com/";
+ return url;
+ }
+
+ /**
+ * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN
+ */
+ private String getWalledGardenPattern() {
+ String pattern = Settings.Secure.getString(mContentResolver,
+ Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN);
+ if (TextUtils.isEmpty(pattern)) return "<title>.*Google.*</title>";
+ return pattern;
+ }
/**
* @see android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE
@@ -509,7 +542,7 @@
// This access point does not require a watchdog, so queue idle on the main thread
mHandler.idle();
}
- mHandler.checkWalledGarden(ssid);
+ if (isWalledGardenTestEnabled()) mHandler.checkWalledGarden(ssid);
}
/**
@@ -1118,19 +1151,35 @@
}
}
+ /**
+ * DNS based detection techniques do not work at all hotspots. The one sure way to check
+ * a walled garden is to see if a URL fetch on a known address fetches the data we
+ * expect
+ */
private boolean isWalledGardenConnection() {
- //One way to detect a walled garden is to see if multiple DNS queries
- //resolve to the same IP address
+ InputStream in = null;
+ HttpURLConnection urlConnection = null;
try {
- String host1 = "www.google.com";
- String host2 = "www.android.com";
- String address1 = InetAddress.getByName(host1).getHostAddress();
- String address2 = InetAddress.getByName(host2).getHostAddress();
- if (address1.equals(address2)) return true;
- } catch (UnknownHostException e) {
+ URL url = new URL(getWalledGardenUrl());
+ urlConnection = (HttpURLConnection) url.openConnection();
+ in = new BufferedInputStream(urlConnection.getInputStream());
+ Scanner scanner = new Scanner(in);
+ if (scanner.findInLine(getWalledGardenPattern()) != null) {
+ return false;
+ } else {
+ return true;
+ }
+ } catch (IOException e) {
return false;
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ if (urlConnection != null) urlConnection.disconnect();
}
- return false;
}
private void handleWalledGardenCheck(String ssid) {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index fba293c..86671d6 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,13 +16,7 @@
package com.android.server.accessibility;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.HandlerCaller.SomeArgs;
-import com.android.server.wm.WindowManagerService;
-
-import org.xmlpull.v1.XmlPullParserException;
-
+import android.Manifest;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceConnection;
@@ -37,7 +31,6 @@
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
@@ -46,15 +39,27 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.IWindow;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.HandlerCaller.SomeArgs;
+import com.android.server.wm.WindowManagerService;
+
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -64,6 +69,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* This class is instantiated by the system as a system level service and can be
@@ -80,12 +86,17 @@
private static final String LOG_TAG = "AccessibilityManagerService";
+ private static final String FUNCTION_REGISTER_EVENT_LISTENER =
+ "registerEventListener";
+
private static int sIdCounter = 0;
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
private static final int DO_SET_SERVICE_INFO = 10;
+ private static int sNextWindowId;
+
final HandlerCaller mCaller;
final Context mContext;
@@ -103,6 +114,11 @@
private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
+ private final SparseArray<IAccessibilityInteractionConnection> mWindowIdToInteractionConnectionMap =
+ new SparseArray<IAccessibilityInteractionConnection>();
+
+ private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>();
+
private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
private PackageManager mPackageManager;
@@ -117,6 +133,10 @@
private boolean mHasInputFilter;
+ private final WindowManagerService mWindowManagerService;
+
+ private final SecurityPolicy mSecurityPolicy;
+
/**
* Handler for delayed event dispatch.
*/
@@ -145,6 +165,9 @@
mContext = context;
mPackageManager = mContext.getPackageManager();
mCaller = new HandlerCaller(context, this);
+ mWindowManagerService = (WindowManagerService) ServiceManager.getService(
+ Context.WINDOW_SERVICE);
+ mSecurityPolicy = new SecurityPolicy();
registerPackageChangeAndBootCompletedBroadcastReceiver();
registerSettingsContentObservers();
@@ -208,7 +231,6 @@
if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
synchronized (mLock) {
populateAccessibilityServiceListLocked();
-
// get the accessibility enabled setting on boot
mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
@@ -223,7 +245,7 @@
return;
}
-
+
super.onReceive(context, intent);
}
};
@@ -297,6 +319,7 @@
public boolean sendAccessibilityEvent(AccessibilityEvent event) {
synchronized (mLock) {
+ mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event);
notifyAccessibilityServicesDelayedLocked(event, false);
notifyAccessibilityServicesDelayedLocked(event, true);
}
@@ -352,7 +375,7 @@
public void executeMessage(Message message) {
switch (message.what) {
- case DO_SET_SERVICE_INFO:
+ case DO_SET_SERVICE_INFO: {
SomeArgs arguments = ((SomeArgs) message.obj);
AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
@@ -365,17 +388,73 @@
AccessibilityServiceInfo oldInfo = service.mAccessibilityServiceInfo;
if (oldInfo != null) {
oldInfo.updateDynamicallyConfigurableProperties(info);
- service.setAccessibilityServiceInfo(oldInfo);
+ service.setDynamicallyConfigurableProperties(oldInfo);
} else {
- service.setAccessibilityServiceInfo(info);
+ service.setDynamicallyConfigurableProperties(info);
}
}
- return;
+ } return;
default:
Slog.w(LOG_TAG, "Unknown message type: " + message.what);
}
}
+ public int addAccessibilityInteractionConnection(IWindow windowToken,
+ IAccessibilityInteractionConnection connection) throws RemoteException {
+ synchronized (mLock) {
+ final IWindow addedWindowToken = windowToken;
+ final int windowId = sNextWindowId++;
+ connection.asBinder().linkToDeath(new DeathRecipient() {
+ public void binderDied() {
+ synchronized (mLock) {
+ removeAccessibilityInteractionConnection(addedWindowToken);
+ }
+ }
+ }, 0);
+ mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder());
+ mWindowIdToInteractionConnectionMap.put(windowId, connection);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId);
+ }
+ return windowId;
+ }
+ }
+
+ public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+ synchronized (mLock) {
+ final int count = mWindowIdToWindowTokenMap.size();
+ for (int i = 0; i < count; i++) {
+ if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) {
+ final int windowId = mWindowIdToWindowTokenMap.keyAt(i);
+ mWindowIdToWindowTokenMap.remove(windowId);
+ mWindowIdToInteractionConnectionMap.remove(windowId);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ public IAccessibilityServiceConnection registerEventListener(IEventListener listener) {
+ mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
+ FUNCTION_REGISTER_EVENT_LISTENER);
+ ComponentName componentName = new ComponentName("foo.bar", "FakeAccessibilityService");
+ synchronized (mLock) {
+ Service oldService = mComponentNameToServiceMap.get(componentName);
+ if (oldService != null) {
+ tryRemoveServiceLocked(oldService);
+ }
+ }
+ AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
+ accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+ accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
+ Service service = new Service(componentName, accessibilityServiceInfo, true);
+ service.onServiceConnected(componentName, listener.asBinder());
+ return service;
+ }
+
/**
* Populates the cached list of installed {@link AccessibilityService}s.
*/
@@ -488,6 +567,12 @@
AccessibilityEvent event = service.mPendingEvents.get(eventType);
try {
+ if (mSecurityPolicy.canRetrieveWindowContent(service)) {
+ event.setConnection(service);
+ } else {
+ event.setSource(null);
+ }
+ event.setSealed(true);
listener.onAccessibilityEvent(event);
if (DEBUG) {
Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
@@ -646,7 +731,7 @@
if (isEnabled) {
if (enabledServices.contains(componentName)) {
if (service == null) {
- service = new Service(installedService);
+ service = new Service(componentName, installedService, false);
}
service.bind();
} else if (!enabledServices.contains(componentName)) {
@@ -683,8 +768,6 @@
* spoken feedback.
*/
private void updateInputFilterLocked() {
- WindowManagerService wm = (WindowManagerService)ServiceManager.getService(
- Context.WINDOW_SERVICE);
if (mIsEnabled) {
final boolean hasSpokenFeedbackServices = !getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty();
@@ -695,19 +778,14 @@
if (mInputFilter == null) {
mInputFilter = new AccessibilityInputFilter(mContext);
}
- wm.setInputFilter(mInputFilter);
+ mWindowManagerService.setInputFilter(mInputFilter);
mHasInputFilter = true;
- } else {
- if (mHasInputFilter) {
- wm.setInputFilter(null);
- mHasInputFilter = false;
- }
+ return;
}
- } else {
- if (mHasInputFilter) {
- wm.setInputFilter(null);
- mHasInputFilter = false;
- }
+ }
+ if (mHasInputFilter) {
+ mWindowManagerService.setInputFilter(null);
+ mHasInputFilter = false;
}
}
@@ -743,24 +821,38 @@
Intent mIntent;
+ boolean mCanRetrieveScreenContent;
+
+ boolean mIsFake;
+
+ final Callback mCallback = new Callback();
+
+ final AtomicInteger mInteractionIdCounter = new AtomicInteger();
+
// the events pending events to be dispatched to this service
final SparseArray<AccessibilityEvent> mPendingEvents =
new SparseArray<AccessibilityEvent>();
- Service(AccessibilityServiceInfo accessibilityServiceInfo) {
+ public Service(ComponentName componentName,
+ AccessibilityServiceInfo accessibilityServiceInfo, boolean isFake) {
mId = sIdCounter++;
- setAccessibilityServiceInfo(accessibilityServiceInfo);
- mIntent = new Intent().setComponent(mComponentName);
- mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
- com.android.internal.R.string.accessibility_binding_label);
- mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
- mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
+ mComponentName = componentName;
+ mAccessibilityServiceInfo = accessibilityServiceInfo;
+ mIsFake = isFake;
+ if (!isFake) {
+ mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent();
+ mIntent = new Intent().setComponent(mComponentName);
+ mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.accessibility_binding_label);
+ mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
+ } else {
+ mCanRetrieveScreenContent = true;
+ }
+ setDynamicallyConfigurableProperties(accessibilityServiceInfo);
}
- public void setAccessibilityServiceInfo(AccessibilityServiceInfo info) {
- mAccessibilityServiceInfo = info;
- ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
- mComponentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
mEventTypes = info.eventTypes;
mFeedbackType = info.feedbackType;
String[] packageNames = info.packageNames;
@@ -781,7 +873,7 @@
* @return True if binding is successful.
*/
public boolean bind() {
- if (mService == null) {
+ if (!mIsFake && mService == null) {
return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
}
return false;
@@ -798,7 +890,9 @@
synchronized (mLock) {
tryRemoveServiceLocked(this);
}
- mContext.unbindService(this);
+ if (!mIsFake) {
+ mContext.unbindService(this);
+ }
mService = null;
return true;
}
@@ -832,6 +926,154 @@
}
}
+ public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId) {
+ IAccessibilityInteractionConnection connection = null;
+ synchronized (mLock) {
+ final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+ if (permissionGranted) {
+ connection = getConnectionToRetrievalAllowingWindowLocked();
+ }
+ }
+ if (connection == null) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "No interaction connection to a retrieve allowing window.");
+ }
+ return null;
+ }
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, mCallback);
+ AccessibilityNodeInfo info = mCallback.getFindAccessibilityNodeInfoResultAndClear(
+ interactionId);
+ if (info != null) {
+ info.setConnection(this);
+ }
+ info.setSealed(true);
+ return info;
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Error finding node.");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ return null;
+ }
+
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text) {
+ IAccessibilityInteractionConnection connection = null;
+ synchronized (mLock) {
+ final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+ if (permissionGranted) {
+ connection = getConnectionToRetrievalAllowingWindowLocked();
+ }
+ }
+ if (connection == null) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "No interaction connection to focused window.");
+ }
+ return null;
+ }
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ connection.findAccessibilityNodeInfosByViewText(text, interactionId, mCallback);
+ List<AccessibilityNodeInfo> infos =
+ mCallback.getFindAccessibilityNodeInfosResultAndClear(interactionId);
+ if (infos != null) {
+ final int infoCount = infos.size();
+ for (int i = 0; i < infoCount; i++) {
+ AccessibilityNodeInfo info = infos.get(i);
+ info.setConnection(this);
+ info.setSealed(true);
+ }
+ }
+ return infos;
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Error finding node.");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ return null;
+ }
+
+ public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
+ int accessibilityWindowId, int accessibilityViewId) {
+ IAccessibilityInteractionConnection connection = null;
+ synchronized (mLock) {
+ final boolean permissionGranted =
+ mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
+ if (permissionGranted) {
+ connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
+ }
+ }
+ if (connection == null) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "No interaction connection to window: "
+ + accessibilityWindowId);
+ }
+ return null;
+ }
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId,
+ interactionId, mCallback);
+ AccessibilityNodeInfo info =
+ mCallback.getFindAccessibilityNodeInfoResultAndClear(interactionId);
+ if (info != null) {
+ info.setConnection(this);
+ info.setSealed(true);
+ }
+ return info;
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
+ + accessibilityViewId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ return null;
+ }
+
+ public boolean performAccessibilityAction(int accessibilityWindowId,
+ int accessibilityViewId, int action) {
+ IAccessibilityInteractionConnection connection = null;
+ synchronized (mLock) {
+ final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
+ accessibilityWindowId, action);
+ if (permissionGranted) {
+ connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
+ }
+ }
+ if (connection == null) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "No interaction connection to window: "
+ + accessibilityWindowId);
+ }
+ return false;
+ }
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ connection.performAccessibilityAction(accessibilityViewId, action, interactionId,
+ mCallback);
+ return mCallback.getPerformAccessibilityActionResult(interactionId);
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
+ + accessibilityViewId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ return false;
+ }
+
public void onServiceDisconnected(ComponentName componentName) {
/* do nothing - #binderDied takes care */
}
@@ -849,5 +1091,171 @@
tryRemoveServiceLocked(this);
}
}
+
+ private IAccessibilityInteractionConnection getConnectionToRetrievalAllowingWindowLocked() {
+ final int windowId = mSecurityPolicy.getRetrievalAllowingWindowLocked();
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
+ }
+ return mWindowIdToInteractionConnectionMap.get(windowId);
+ }
+ }
+
+ final class SecurityPolicy {
+ private static final int VALID_ACTIONS = AccessibilityNodeInfo.ACTION_FOCUS
+ | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS | AccessibilityNodeInfo.ACTION_SELECT
+ | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
+
+ private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
+ AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END
+ | AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START
+ | AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED
+ | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+ | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+ | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
+ private int mRetrievalAlowingWindowId;
+
+ public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) {
+ final int windowId = event.getSourceAccessibilityWindowId();
+ final int eventType = event.getEventType();
+ if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) != 0) {
+ mRetrievalAlowingWindowId = windowId;
+ } else {
+ event.setSource(null);
+ }
+ }
+
+ public int getRetrievalAllowingWindowLocked() {
+ return mRetrievalAlowingWindowId;
+ }
+
+ public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
+ return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
+ }
+
+ public boolean canPerformActionLocked(Service service, int windowId, int action) {
+ return canRetrieveWindowContent(service)
+ && isRetrievalAllowingWindow(windowId)
+ && isActionPermitted(action);
+ }
+
+ public boolean canRetrieveWindowContent(Service service) {
+ return service.mCanRetrieveScreenContent;
+ }
+
+ private boolean isRetrievalAllowingWindow(int windowId) {
+ return (mRetrievalAlowingWindowId == windowId);
+ }
+
+ private boolean isActionPermitted(int action) {
+ return (VALID_ACTIONS & action) != 0;
+ }
+
+ private void enforceCallingPermission(String permission, String function) {
+ if (OWN_PROCESS_ID == Binder.getCallingPid()) {
+ return;
+ }
+ final int permissionStatus = mContext.checkCallingPermission(permission);
+ if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You do not have " + permission
+ + " required to call " + function);
+ }
+ }
+ }
+
+ final class Callback extends IAccessibilityInteractionConnectionCallback.Stub {
+ private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
+
+ private int mInteractionId = -1;
+ private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
+ private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
+ private boolean mPerformAccessibilityActionResult;
+
+ public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
+ int interactionId) {
+ synchronized (mLock) {
+ if (interactionId > mInteractionId) {
+ mFindAccessibilityNodeInfoResult = info;
+ mInteractionId = interactionId;
+ }
+ mLock.notifyAll();
+ }
+ }
+
+ public AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
+ synchronized (mLock) {
+ waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
+ AccessibilityNodeInfo result = mFindAccessibilityNodeInfoResult;
+ clearLocked();
+ return result;
+ }
+ }
+
+ public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
+ int interactionId) {
+ synchronized (mLock) {
+ if (interactionId > mInteractionId) {
+ mFindAccessibilityNodeInfosResult = infos;
+ mInteractionId = interactionId;
+ }
+ mLock.notifyAll();
+ }
+ }
+
+ public List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
+ int interactionId) {
+ synchronized (mLock) {
+ waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
+ List<AccessibilityNodeInfo> result = mFindAccessibilityNodeInfosResult;
+ clearLocked();
+ return result;
+ }
+ }
+
+ public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
+ synchronized (mLock) {
+ if (interactionId > mInteractionId) {
+ mPerformAccessibilityActionResult = succeeded;
+ mInteractionId = interactionId;
+ }
+ mLock.notifyAll();
+ }
+ }
+
+ public boolean getPerformAccessibilityActionResult(int interactionId) {
+ synchronized (mLock) {
+ waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
+ final boolean result = mPerformAccessibilityActionResult;
+ clearLocked();
+ return result;
+ }
+ }
+
+ public void clearLocked() {
+ mInteractionId = -1;
+ mFindAccessibilityNodeInfoResult = null;
+ mFindAccessibilityNodeInfosResult = null;
+ mPerformAccessibilityActionResult = false;
+ }
+
+ private void waitForResultTimedLocked(long waitTimeMillis, int interactionId) {
+ final long startTimeMillis = SystemClock.uptimeMillis();
+ while (true) {
+ try {
+ if (mInteractionId == interactionId) {
+ return;
+ }
+ final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+ waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
+ if (waitTimeMillis <= 0) {
+ return;
+ }
+ mLock.wait(waitTimeMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
}
}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 4c7f595..aab189a6 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -226,8 +226,9 @@
// Send a hover for every finger down so the user gets feedback
// where she is currently touching.
mSendHoverDelayed.forceSendAndRemove();
- mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, 1, policyFlags,
- DELAY_SEND_HOVER_MOVE);
+ final int pointerIdBits = (1 << event.getActionIndex());
+ mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits,
+ policyFlags, DELAY_SEND_HOVER_MOVE);
} break;
case MotionEvent.ACTION_POINTER_DOWN: {
switch (activePointerCount) {
@@ -350,11 +351,12 @@
if (isDraggingGesture(event)) {
// Two pointers moving in the same direction within
// a given distance perform a drag.
- mCurrentState = STATE_DRAGGING;
+ mCurrentState = STATE_DRAGGING;
if (mTouchExploreGestureInProgress) {
sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
mTouchExploreGestureInProgress = false;
}
+ mLastTouchExploreEvent = null;
mDraggingPointerId = pointerId;
sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
policyFlags);
@@ -365,6 +367,7 @@
sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
mTouchExploreGestureInProgress = false;
}
+ mLastTouchExploreEvent = null;
sendDownForAllActiveNotInjectedPointers(event, policyFlags);
}
} break;
@@ -382,6 +385,7 @@
sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
mTouchExploreGestureInProgress = false;
}
+ mLastTouchExploreEvent = null;
sendDownForAllActiveNotInjectedPointers(event, policyFlags);
}
}
@@ -728,7 +732,7 @@
/**
* Sends an event.
*
- * @param event The event to send.
+ * @param prototype The prototype from which to create the injected events.
* @param action The action of the event.
* @param pointerIdBits The bits of the pointers to send.
* @param policyFlags The policy flags associated with the event.
@@ -979,7 +983,7 @@
case MotionEvent.ACTION_DOWN: {
// New gesture so restart tracking injected down pointers.
mInjectedPointersDown = 0;
- handleReceivedPointerDown(0, event);
+ handleReceivedPointerDown(event.getActionIndex(), event);
} break;
case MotionEvent.ACTION_POINTER_DOWN: {
handleReceivedPointerDown(event.getActionIndex(), event);
@@ -988,7 +992,7 @@
handleReceivedPointerMove(event);
} break;
case MotionEvent.ACTION_UP: {
- handleReceivedPointerUp(0, event);
+ handleReceivedPointerUp(event.getActionIndex(), event);
} break;
case MotionEvent.ACTION_POINTER_UP: {
handleReceivedPointerUp(event.getActionIndex(), event);
@@ -1008,13 +1012,13 @@
final int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN: {
- handleInjectedPointerDown(0, event);
+ handleInjectedPointerDown(event.getActionIndex(), event);
} break;
case MotionEvent.ACTION_POINTER_DOWN: {
handleInjectedPointerDown(event.getActionIndex(), event);
} break;
case MotionEvent.ACTION_UP: {
- handleInjectedPointerUp(0, event);
+ handleInjectedPointerUp(event.getActionIndex(), event);
} break;
case MotionEvent.ACTION_POINTER_UP: {
handleInjectedPointerUp(event.getActionIndex(), event);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c2a8a1d..da9f1d6 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -92,7 +92,6 @@
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IInterface;
import android.os.IPermissionController;
import android.os.Looper;
import android.os.Message;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index e8155b4..5fa26ef 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -129,13 +129,6 @@
mNMService = nmService;
mLooper = looper;
- // register for notifications from NetworkManagement Service
- try {
- mNMService.registerObserver(this);
- } catch (RemoteException e) {
- Log.e(TAG, "Error registering observer :" + e);
- }
-
mIfaces = new HashMap<String, TetherInterfaceSM>();
// make our own thread so we don't anr the system
@@ -1212,8 +1205,8 @@
return null;
}
- for (String iface : ifaces) {
- for (String regex : mUpstreamIfaceRegexs) {
+ for (String regex : mUpstreamIfaceRegexs) {
+ for (String iface : ifaces) {
if (iface.matches(regex)) {
// verify it is active
InterfaceConfiguration ifcg = null;
diff --git a/services/java/com/android/server/net/InterfaceIdentity.java b/services/java/com/android/server/net/InterfaceIdentity.java
new file mode 100644
index 0000000..ff86581
--- /dev/null
+++ b/services/java/com/android/server/net/InterfaceIdentity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.util.HashSet;
+
+/**
+ * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
+ * active on that interface.
+ *
+ * @hide
+ */
+public class InterfaceIdentity extends HashSet<NetworkIdentity> {
+ private static final int VERSION_CURRENT = 1;
+
+ public InterfaceIdentity() {
+ }
+
+ public InterfaceIdentity(DataInputStream in) throws IOException {
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_CURRENT: {
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ add(new NetworkIdentity(in));
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ }
+
+ public void writeToStream(DataOutputStream out) throws IOException {
+ out.writeInt(VERSION_CURRENT);
+ out.writeInt(size());
+ for (NetworkIdentity ident : this) {
+ ident.writeToStream(out);
+ }
+ }
+
+ /**
+ * Test if any {@link NetworkIdentity} on this interface matches the given
+ * template and IMEI.
+ */
+ public boolean matchesTemplate(int networkTemplate, String subscriberId) {
+ for (NetworkIdentity ident : this) {
+ if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkIdentity.java b/services/java/com/android/server/net/NetworkIdentity.java
new file mode 100644
index 0000000..79feb95
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkIdentity.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.isNetworkTypeMobile;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_4G;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
+import static android.net.TrafficStats.TEMPLATE_WIFI;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
+import static android.telephony.TelephonyManager.getNetworkClass;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Objects;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+
+/**
+ * Identity of a {@link NetworkInfo}, defined by network type and billing
+ * relationship (such as IMSI).
+ *
+ * @hide
+ */
+public class NetworkIdentity {
+ private static final int VERSION_CURRENT = 1;
+
+ public final int type;
+ public final int subType;
+ public final String subscriberId;
+
+ public NetworkIdentity(int type, int subType, String subscriberId) {
+ this.type = type;
+ this.subType = subType;
+ this.subscriberId = subscriberId;
+ }
+
+ public NetworkIdentity(DataInputStream in) throws IOException {
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_CURRENT: {
+ type = in.readInt();
+ subType = in.readInt();
+ subscriberId = in.readUTF();
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ }
+
+ public void writeToStream(DataOutputStream out) throws IOException {
+ out.writeInt(VERSION_CURRENT);
+ out.writeInt(type);
+ out.writeInt(subType);
+ out.writeUTF(subscriberId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(type, subType, subscriberId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof NetworkIdentity) {
+ final NetworkIdentity ident = (NetworkIdentity) obj;
+ return type == ident.type && subType == ident.subType
+ && Objects.equal(subscriberId, ident.subscriberId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final String typeName = ConnectivityManager.getNetworkTypeName(type);
+ final String subTypeName;
+ if (ConnectivityManager.isNetworkTypeMobile(type)) {
+ subTypeName = TelephonyManager.getNetworkTypeName(subType);
+ } else {
+ subTypeName = Integer.toString(subType);
+ }
+
+ return "[type=" + typeName + ", subType=" + subTypeName + ", subId=" + subscriberId + "]";
+ }
+
+ /**
+ * Test if this network matches the given template and IMEI.
+ */
+ public boolean matchesTemplate(int networkTemplate, String subscriberId) {
+ switch (networkTemplate) {
+ case TEMPLATE_MOBILE_ALL:
+ return matchesMobile(subscriberId);
+ case TEMPLATE_MOBILE_3G_LOWER:
+ return matchesMobile3gLower(subscriberId);
+ case TEMPLATE_MOBILE_4G:
+ return matchesMobile4g(subscriberId);
+ case TEMPLATE_WIFI:
+ return matchesWifi();
+ default:
+ throw new IllegalArgumentException("unknown network template");
+ }
+ }
+
+ /**
+ * Check if mobile network with matching IMEI. Also matches
+ * {@link #TYPE_WIMAX}.
+ */
+ private boolean matchesMobile(String subscriberId) {
+ if (isNetworkTypeMobile(type) && Objects.equal(this.subscriberId, subscriberId)) {
+ return true;
+ } else if (type == TYPE_WIMAX) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if mobile network classified 3G or lower with matching IMEI.
+ */
+ private boolean matchesMobile3gLower(String subscriberId) {
+ if (isNetworkTypeMobile(type)
+ && Objects.equal(this.subscriberId, subscriberId)) {
+ switch (getNetworkClass(subType)) {
+ case NETWORK_CLASS_2_G:
+ case NETWORK_CLASS_3_G:
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if mobile network classified 4G with matching IMEI. Also matches
+ * {@link #TYPE_WIMAX}.
+ */
+ private boolean matchesMobile4g(String subscriberId) {
+ if (isNetworkTypeMobile(type)
+ && Objects.equal(this.subscriberId, subscriberId)) {
+ switch (getNetworkClass(subType)) {
+ case NETWORK_CLASS_4_G:
+ return true;
+ }
+ } else if (type == TYPE_WIMAX) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if matches Wi-Fi network template.
+ */
+ private boolean matchesWifi() {
+ if (type == TYPE_WIFI) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Build a {@link NetworkIdentity} from the given {@link NetworkState},
+ * assuming that any mobile networks are using the current IMSI.
+ */
+ public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) {
+ final int type = state.networkInfo.getType();
+ final int subType = state.networkInfo.getSubtype();
+
+ // TODO: consider moving subscriberId over to LinkCapabilities, so it
+ // comes from an authoritative source.
+
+ final String subscriberId;
+ if (isNetworkTypeMobile(type)) {
+ final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ subscriberId = telephony.getSubscriberId();
+ } else {
+ subscriberId = null;
+ }
+ return new NetworkIdentity(type, subType, subscriberId);
+ }
+
+}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 17c7161..1a90a92 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -89,6 +89,8 @@
// TODO: keep record of billing cycle details, and limit rules
// TODO: keep map of interfaces-to-billing-relationship
+ // TODO: dispatch callbacks through handler when locked
+
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
IPowerManager powerManager, INetworkStatsService networkStats) {
mContext = checkNotNull(context, "missing context");
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index d9c1f25..3892de8 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2011 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.
@@ -16,12 +16,23 @@
package com.android.server.net;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.SHUTDOWN;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.UID_ALL;
+import static android.provider.Settings.Secure.NETSTATS_DETAIL_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_DETAIL_MAX_HISTORY;
+import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
+import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
+import static android.provider.Settings.Secure.NETSTATS_SUMMARY_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_SUMMARY_MAX_HISTORY;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.internal.util.Preconditions.checkNotNull;
import android.app.AlarmManager;
import android.app.IAlarmManager;
@@ -30,29 +41,33 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.net.IConnectivityManager;
import android.net.INetworkStatsService;
-import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
-import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.provider.Settings;
import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
import android.util.NtpTrustedTime;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TrustedTime;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.TelephonyIntents;
+import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
/**
* Collect and persist detailed network statistics, and provide this data to
@@ -67,39 +82,49 @@
private final IAlarmManager mAlarmManager;
private final TrustedTime mTime;
+ private IConnectivityManager mConnManager;
+
private static final String ACTION_NETWORK_STATS_POLL =
"com.android.server.action.NETWORK_STATS_POLL";
private PendingIntent mPollIntent;
- // TODO: move tweakable params to Settings.Secure
// TODO: listen for kernel push events through netd instead of polling
private static final long KB_IN_BYTES = 1024;
+ private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
+ private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
- private static final long POLL_INTERVAL = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
- private static final long SUMMARY_BUCKET_DURATION = 6 * DateUtils.HOUR_IN_MILLIS;
- private static final long SUMMARY_MAX_HISTORY = 90 * DateUtils.DAY_IN_MILLIS;
+ private LongSecureSetting mPollInterval = new LongSecureSetting(
+ NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
+ private LongSecureSetting mPersistThreshold = new LongSecureSetting(
+ NETSTATS_PERSIST_THRESHOLD, 64 * KB_IN_BYTES);
- // TODO: remove these high-frequency testing values
-// private static final long POLL_INTERVAL = 5 * DateUtils.SECOND_IN_MILLIS;
-// private static final long SUMMARY_BUCKET_DURATION = 10 * DateUtils.SECOND_IN_MILLIS;
-// private static final long SUMMARY_MAX_HISTORY = 2 * DateUtils.MINUTE_IN_MILLIS;
+ private LongSecureSetting mSummaryBucketDuration = new LongSecureSetting(
+ NETSTATS_SUMMARY_BUCKET_DURATION, 6 * HOUR_IN_MILLIS);
+ private LongSecureSetting mSummaryMaxHistory = new LongSecureSetting(
+ NETSTATS_SUMMARY_MAX_HISTORY, 90 * DAY_IN_MILLIS);
+ private LongSecureSetting mDetailBucketDuration = new LongSecureSetting(
+ NETSTATS_DETAIL_BUCKET_DURATION, 6 * HOUR_IN_MILLIS);
+ private LongSecureSetting mDetailMaxHistory = new LongSecureSetting(
+ NETSTATS_DETAIL_MAX_HISTORY, 90 * DAY_IN_MILLIS);
- /** Minimum delta required to persist to disk. */
- private static final long SUMMARY_PERSIST_THRESHOLD = 64 * KB_IN_BYTES;
-
- private static final long TIME_CACHE_MAX_AGE = DateUtils.DAY_IN_MILLIS;
+ private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
private final Object mStatsLock = new Object();
/** Set of active ifaces during this boot. */
- private HashMap<String, InterfaceInfo> mActiveIface = Maps.newHashMap();
- /** Set of historical stats for known ifaces. */
- private HashMap<InterfaceInfo, NetworkStatsHistory> mIfaceStats = Maps.newHashMap();
+ private HashMap<String, InterfaceIdentity> mActiveIface = Maps.newHashMap();
- private NetworkStats mLastPollStats;
- private NetworkStats mLastPersistStats;
+ /** Set of historical stats for known ifaces. */
+ private HashMap<InterfaceIdentity, NetworkStatsHistory> mSummaryStats = Maps.newHashMap();
+ /** Set of historical stats for known UIDs. */
+ private SparseArray<NetworkStatsHistory> mDetailStats = new SparseArray<NetworkStatsHistory>();
+
+ private NetworkStats mLastSummaryPoll;
+ private NetworkStats mLastSummaryPersist;
+
+ private NetworkStats mLastDetailPoll;
private final HandlerThread mHandlerThread;
private final Handler mHandler;
@@ -132,13 +157,9 @@
readStatsLocked();
// watch other system services that claim interfaces
- // TODO: protect incoming broadcast with permissions check.
- // TODO: consider migrating this to ConnectivityService, but it might
- // cause a circular dependency.
- final IntentFilter interfaceFilter = new IntentFilter();
- interfaceFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
- interfaceFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- mContext.registerReceiver(mInterfaceReceiver, interfaceFilter);
+ final IntentFilter ifaceFilter = new IntentFilter();
+ ifaceFilter.addAction(CONNECTIVITY_ACTION);
+ mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
// listen for periodic polling events
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
@@ -155,9 +176,13 @@
}
}
+ public void bindConnectivityManager(IConnectivityManager connManager) {
+ mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
+ }
+
/**
* Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and
- * reschedule based on current {@link #POLL_INTERVAL} value.
+ * reschedule based on current {@link #mPollInterval} value.
*/
private void registerPollAlarmLocked() throws RemoteException {
if (mPollIntent != null) {
@@ -169,49 +194,72 @@
final long currentRealtime = SystemClock.elapsedRealtime();
mAlarmManager.setInexactRepeating(
- AlarmManager.ELAPSED_REALTIME, currentRealtime, POLL_INTERVAL, mPollIntent);
+ AlarmManager.ELAPSED_REALTIME, currentRealtime, mPollInterval.get(), mPollIntent);
}
@Override
- public NetworkStatsHistory[] getNetworkStatsSummary(int networkType) {
- // TODO: return history for requested types
- return null;
+ public NetworkStatsHistory getHistoryForNetwork(int networkTemplate) {
+ // TODO: create relaxed permission for reading stats
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+ synchronized (mStatsLock) {
+ // combine all interfaces that match template
+ final String subscriberId = getActiveSubscriberId();
+ final NetworkStatsHistory combined = new NetworkStatsHistory(
+ mSummaryBucketDuration.get());
+ for (InterfaceIdentity ident : mSummaryStats.keySet()) {
+ final NetworkStatsHistory history = mSummaryStats.get(ident);
+ if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+ combined.recordEntireHistory(history);
+ }
+ }
+ return combined;
+ }
}
@Override
- public NetworkStatsHistory getNetworkStatsUid(int uid) {
+ public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) {
+ // TODO: create relaxed permission for reading stats
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
// TODO: return history for requested uid
return null;
}
+ @Override
+ public NetworkStats getSummaryPerUid(long start, long end, int networkTemplate) {
+ // TODO: create relaxed permission for reading stats
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+ // TODO: apply networktemplate once granular uid stats are stored.
+
+ synchronized (mStatsLock) {
+ final int size = mDetailStats.size();
+ final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size);
+
+ final long[] total = new long[2];
+ for (int i = 0; i < size; i++) {
+ final int uid = mDetailStats.keyAt(i);
+ final NetworkStatsHistory history = mDetailStats.valueAt(i);
+ history.getTotalData(start, end, total);
+ stats.addEntry(IFACE_ALL, uid, total[0], total[1]);
+ }
+ return stats.build();
+ }
+ }
+
/**
- * Receiver that watches for other system components that claim network
+ * Receiver that watches for {@link IConnectivityManager} to claim network
* interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
* with mobile interfaces.
*/
- private BroadcastReceiver mInterfaceReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED.equals(action)) {
- final LinkProperties prop = intent.getParcelableExtra(
- Phone.DATA_LINK_PROPERTIES_KEY);
- final String iface = prop != null ? prop.getInterfaceName() : null;
- if (iface != null) {
- final TelephonyManager teleManager = (TelephonyManager) context
- .getSystemService(Context.TELEPHONY_SERVICE);
- final InterfaceInfo info = new InterfaceInfo(
- iface, TYPE_MOBILE, teleManager.getSubscriberId());
- reportActiveInterface(info);
- }
- } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
- final LinkProperties prop = intent.getParcelableExtra(
- WifiManager.EXTRA_LINK_PROPERTIES);
- final String iface = prop != null ? prop.getInterfaceName() : null;
- if (iface != null) {
- final InterfaceInfo info = new InterfaceInfo(iface, TYPE_WIFI, null);
- reportActiveInterface(info);
- }
+ // on background handler thread, and verified CONNECTIVITY_INTERNAL
+ // permission above.
+ synchronized (mStatsLock) {
+ updateIfacesLocked();
}
}
};
@@ -219,8 +267,8 @@
private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // already running on background handler, network/io is safe, and
- // caller verified to have UPDATE_DEVICE_STATS permission above.
+ // on background handler thread, and verified UPDATE_DEVICE_STATS
+ // permission above.
synchronized (mStatsLock) {
// TODO: acquire wakelock while performing poll
performPollLocked();
@@ -231,13 +279,49 @@
private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // persist stats during clean shutdown
+ // verified SHUTDOWN permission above.
synchronized (mStatsLock) {
writeStatsLocked();
}
}
};
+ /**
+ * Inspect all current {@link NetworkState} to derive mapping from {@code
+ * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
+ * are active on a single {@code iface}, they are combined under a single
+ * {@link InterfaceIdentity}.
+ */
+ private void updateIfacesLocked() {
+ if (LOGD) Slog.v(TAG, "updateIfacesLocked()");
+
+ // take one last stats snapshot before updating iface mapping. this
+ // isn't perfect, since the kernel may already be counting traffic from
+ // the updated network.
+ // TODO: verify that we only poll summary stats, not uid details
+ performPollLocked();
+
+ final NetworkState[] states;
+ try {
+ states = mConnManager.getAllNetworkState();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "problem reading network state");
+ return;
+ }
+
+ // rebuild active interfaces based on connected networks
+ mActiveIface.clear();
+
+ for (NetworkState state : states) {
+ if (state.networkInfo.isConnected()) {
+ // collect networks under their parent interfaces
+ final String iface = state.linkProperties.getInterfaceName();
+ final InterfaceIdentity ident = findOrCreateInterfaceLocked(iface);
+ ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state));
+ }
+ }
+ }
+
private void performPollLocked() {
if (LOGD) Slog.v(TAG, "performPollLocked()");
@@ -250,58 +334,110 @@
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
- final NetworkStats current;
+ final NetworkStats summary;
+ final NetworkStats detail;
try {
- current = mNetworkManager.getNetworkStatsSummary();
+ summary = mNetworkManager.getNetworkStatsSummary();
+ detail = mNetworkManager.getNetworkStatsDetail();
} catch (RemoteException e) {
Slog.w(TAG, "problem reading network stats");
return;
}
- // update historical usage with delta since last poll
- final NetworkStats pollDelta = computeStatsDelta(mLastPollStats, current);
- final long timeStart = currentTime - pollDelta.elapsedRealtime;
- for (String iface : pollDelta.getKnownIfaces()) {
- final InterfaceInfo info = mActiveIface.get(iface);
- if (info == null) {
- if (LOGD) Slog.w(TAG, "unknown interface " + iface + ", ignoring stats");
- continue;
- }
-
- final int index = pollDelta.findIndex(iface, UID_ALL);
- final long rx = pollDelta.rx[index];
- final long tx = pollDelta.tx[index];
-
- final NetworkStatsHistory history = findOrCreateHistoryLocked(info);
- history.recordData(timeStart, currentTime, rx, tx);
- history.removeBucketsBefore(currentTime - SUMMARY_MAX_HISTORY);
- }
-
- mLastPollStats = current;
+ performSummaryPollLocked(summary, currentTime);
+ performDetailPollLocked(detail, currentTime);
// decide if enough has changed to trigger persist
- final NetworkStats persistDelta = computeStatsDelta(mLastPersistStats, current);
- for (String iface : persistDelta.getKnownIfaces()) {
+ final NetworkStats persistDelta = computeStatsDelta(mLastSummaryPersist, summary);
+ final long persistThreshold = mPersistThreshold.get();
+ for (String iface : persistDelta.getUniqueIfaces()) {
final int index = persistDelta.findIndex(iface, UID_ALL);
- if (persistDelta.rx[index] > SUMMARY_PERSIST_THRESHOLD
- || persistDelta.tx[index] > SUMMARY_PERSIST_THRESHOLD) {
+ if (persistDelta.rx[index] > persistThreshold
+ || persistDelta.tx[index] > persistThreshold) {
writeStatsLocked();
- mLastPersistStats = current;
+ mLastSummaryPersist = summary;
break;
}
}
}
- private NetworkStatsHistory findOrCreateHistoryLocked(InterfaceInfo info) {
- NetworkStatsHistory stats = mIfaceStats.get(info);
+ /**
+ * Update {@link #mSummaryStats} historical usage.
+ */
+ private void performSummaryPollLocked(NetworkStats summary, long currentTime) {
+ final ArrayList<String> unknownIface = Lists.newArrayList();
+
+ final NetworkStats delta = computeStatsDelta(mLastSummaryPoll, summary);
+ final long timeStart = currentTime - delta.elapsedRealtime;
+ final long maxHistory = mSummaryMaxHistory.get();
+ for (String iface : delta.getUniqueIfaces()) {
+ final InterfaceIdentity ident = mActiveIface.get(iface);
+ if (ident == null) {
+ unknownIface.add(iface);
+ continue;
+ }
+
+ final int index = delta.findIndex(iface, UID_ALL);
+ final long rx = delta.rx[index];
+ final long tx = delta.tx[index];
+
+ final NetworkStatsHistory history = findOrCreateSummaryLocked(ident);
+ history.recordData(timeStart, currentTime, rx, tx);
+ history.removeBucketsBefore(currentTime - maxHistory);
+ }
+ mLastSummaryPoll = summary;
+
+ if (LOGD && unknownIface.size() > 0) {
+ Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
+ }
+ }
+
+ /**
+ * Update {@link #mDetailStats} historical usage.
+ */
+ private void performDetailPollLocked(NetworkStats detail, long currentTime) {
+ final NetworkStats delta = computeStatsDelta(mLastDetailPoll, detail);
+ final long timeStart = currentTime - delta.elapsedRealtime;
+ final long maxHistory = mDetailMaxHistory.get();
+ for (int uid : delta.getUniqueUids()) {
+ final int index = delta.findIndex(IFACE_ALL, uid);
+ final long rx = delta.rx[index];
+ final long tx = delta.tx[index];
+
+ final NetworkStatsHistory history = findOrCreateDetailLocked(uid);
+ history.recordData(timeStart, currentTime, rx, tx);
+ history.removeBucketsBefore(currentTime - maxHistory);
+ }
+ mLastDetailPoll = detail;
+ }
+
+ private NetworkStatsHistory findOrCreateSummaryLocked(InterfaceIdentity ident) {
+ NetworkStatsHistory stats = mSummaryStats.get(ident);
if (stats == null) {
- stats = new NetworkStatsHistory(
- info.networkType, info.identity, UID_ALL, SUMMARY_BUCKET_DURATION);
- mIfaceStats.put(info, stats);
+ stats = new NetworkStatsHistory(mSummaryBucketDuration.get());
+ mSummaryStats.put(ident, stats);
}
return stats;
}
+ private NetworkStatsHistory findOrCreateDetailLocked(int uid) {
+ NetworkStatsHistory stats = mDetailStats.get(uid);
+ if (stats == null) {
+ stats = new NetworkStatsHistory(mDetailBucketDuration.get());
+ mDetailStats.put(uid, stats);
+ }
+ return stats;
+ }
+
+ private InterfaceIdentity findOrCreateInterfaceLocked(String iface) {
+ InterfaceIdentity ident = mActiveIface.get(iface);
+ if (ident == null) {
+ ident = new InterfaceIdentity();
+ mActiveIface.put(iface, ident);
+ }
+ return ident;
+ }
+
private void readStatsLocked() {
if (LOGD) Slog.v(TAG, "readStatsLocked()");
// TODO: read historical stats from disk using AtomicFile
@@ -310,70 +446,96 @@
private void writeStatsLocked() {
if (LOGD) Slog.v(TAG, "writeStatsLocked()");
// TODO: persist historical stats to disk using AtomicFile
+ // TODO: consider duplicating stats and releasing lock while writing
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
- pw.println("Active interfaces:");
- for (InterfaceInfo info : mActiveIface.values()) {
- info.dump(" ", pw);
+ final HashSet<String> argSet = new HashSet<String>();
+ for (String arg : args) {
+ argSet.add(arg);
}
- pw.println("Known historical stats:");
- for (NetworkStatsHistory stats : mIfaceStats.values()) {
- stats.dump(" ", pw);
+ synchronized (mStatsLock) {
+ // TODO: remove this testing code, since it corrupts stats
+ if (argSet.contains("generate")) {
+ generateRandomLocked();
+ pw.println("Generated stub stats");
+ return;
+ }
+
+ pw.println("Active interfaces:");
+ for (String iface : mActiveIface.keySet()) {
+ final InterfaceIdentity ident = mActiveIface.get(iface);
+ pw.print(" iface="); pw.print(iface);
+ pw.print(" ident="); pw.println(ident.toString());
+ }
+
+ pw.println("Known historical stats:");
+ for (InterfaceIdentity ident : mSummaryStats.keySet()) {
+ final NetworkStatsHistory stats = mSummaryStats.get(ident);
+ pw.print(" ident="); pw.println(ident.toString());
+ stats.dump(" ", pw);
+ }
+
+ if (argSet.contains("detail")) {
+ pw.println("Known detail stats:");
+ for (int i = 0; i < mDetailStats.size(); i++) {
+ final int uid = mDetailStats.keyAt(i);
+ final NetworkStatsHistory stats = mDetailStats.valueAt(i);
+ pw.print(" UID="); pw.println(uid);
+ stats.dump(" ", pw);
+ }
+ }
}
}
/**
- * Details for a well-known network interface, including its name, network
- * type, and billing relationship identity (such as IMSI).
+ * @deprecated only for temporary testing
*/
- private static class InterfaceInfo {
- public final String iface;
- public final int networkType;
- public final String identity;
+ @Deprecated
+ private void generateRandomLocked() {
+ long end = System.currentTimeMillis();
+ long start = end - mSummaryMaxHistory.get();
+ long rx = 3 * GB_IN_BYTES;
+ long tx = 2 * GB_IN_BYTES;
- public InterfaceInfo(String iface, int networkType, String identity) {
- this.iface = iface;
- this.networkType = networkType;
- this.identity = identity;
+ mSummaryStats.clear();
+ for (InterfaceIdentity ident : mActiveIface.values()) {
+ final NetworkStatsHistory stats = findOrCreateSummaryLocked(ident);
+ stats.generateRandom(start, end, rx, tx);
}
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((identity == null) ? 0 : identity.hashCode());
- result = prime * result + ((iface == null) ? 0 : iface.hashCode());
- result = prime * result + networkType;
- return result;
- }
+ end = System.currentTimeMillis();
+ start = end - mDetailMaxHistory.get();
+ rx = 500 * MB_IN_BYTES;
+ tx = 100 * MB_IN_BYTES;
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof InterfaceInfo) {
- final InterfaceInfo info = (InterfaceInfo) obj;
- return equal(iface, info.iface) && networkType == info.networkType
- && equal(identity, info.identity);
- }
- return false;
- }
-
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix);
- pw.print("InterfaceInfo: iface="); pw.print(iface);
- pw.print(" networkType="); pw.print(networkType);
- pw.print(" identity="); pw.println(identity);
+ mDetailStats.clear();
+ for (ApplicationInfo info : mContext.getPackageManager().getInstalledApplications(0)) {
+ final int uid = info.uid;
+ final NetworkStatsHistory stats = findOrCreateDetailLocked(uid);
+ stats.generateRandom(start, end, rx, tx);
}
}
- private void reportActiveInterface(InterfaceInfo info) {
- synchronized (mStatsLock) {
- // TODO: when interface redefined, port over historical stats
- mActiveIface.put(info.iface, info);
+ private class LongSecureSetting {
+ private String mKey;
+ private long mDefaultValue;
+
+ public LongSecureSetting(String key, long defaultValue) {
+ mKey = key;
+ mDefaultValue = defaultValue;
+ }
+
+ public long get() {
+ if (mContext != null) {
+ return Settings.Secure.getLong(mContext.getContentResolver(), mKey, mDefaultValue);
+ } else {
+ return mDefaultValue;
+ }
}
}
@@ -389,15 +551,10 @@
}
}
- private static boolean equal(Object a, Object b) {
- return a == b || (a != null && a.equals(b));
- }
-
- private static <T> T checkNotNull(T value, String message) {
- if (value == null) {
- throw new NullPointerException(message);
- }
- return value;
+ private String getActiveSubscriberId() {
+ final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ return telephony.getSubscriberId();
}
}
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 3095c37..ee69311 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -617,8 +617,13 @@
}
@SuppressWarnings("unused")
- public int getTapTimeout() {
- return ViewConfiguration.getTapTimeout();
+ public int getHoverTapTimeout() {
+ return ViewConfiguration.getHoverTapTimeout();
+ }
+
+ @SuppressWarnings("unused")
+ public int getHoverTapSlop() {
+ return ViewConfiguration.getHoverTapSlop();
}
@SuppressWarnings("unused")
@@ -632,11 +637,6 @@
}
@SuppressWarnings("unused")
- public int getTouchSlop() {
- return ViewConfiguration.get(mContext).getScaledTouchSlop();
- }
-
- @SuppressWarnings("unused")
public int getMaxEventsPerSecond() {
int result = 0;
try {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index d95d4c5..819c16e 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -153,6 +153,7 @@
static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_TOKEN_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
+ static final boolean DEBUG_APP_ORIENTATION = false;
static final boolean DEBUG_CONFIGURATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
@@ -427,6 +428,7 @@
boolean mWindowsFreezingScreen = false;
long mFreezeGcPending = 0;
int mAppsFreezingScreen = 0;
+ int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
int mLayoutSeq = 0;
@@ -3187,6 +3189,15 @@
}
public int getOrientationFromWindowsLocked() {
+ if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
+ // If the display is frozen, some activities may be in the middle
+ // of restarting, and thus have removed their old window. If the
+ // window has the flag to hide the lock screen, then the lock screen
+ // can re-appear and inflict its own orientation on us. Keep the
+ // orientation stable until this all settles down.
+ return mLastWindowForcedOrientation;
+ }
+
int pos = mWindows.size() - 1;
while (pos >= 0) {
WindowState wtoken = mWindows.get(pos);
@@ -3194,7 +3205,7 @@
if (wtoken.mAppToken != null) {
// We hit an application window. so the orientation will be determined by the
// app window. No point in continuing further.
- return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
if (!wtoken.isVisibleLw() || !wtoken.mPolicyVisibilityAfterAnim) {
continue;
@@ -3204,10 +3215,10 @@
(req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
continue;
} else {
- return req;
+ return (mLastWindowForcedOrientation=req);
}
}
- return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
public int getOrientationFromAppTokensLocked() {
@@ -3220,16 +3231,23 @@
while (pos >= 0) {
AppWindowToken wtoken = mAppTokens.get(pos);
pos--;
+
+ if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + wtoken);
+
// if we're about to tear down this window and not seek for
// the behind activity, don't use it for orientation
if (!findingBehind
&& (!wtoken.hidden && wtoken.hiddenRequested)) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+ + " -- going to hide");
continue;
}
if (!haveGroup) {
// We ignore any hidden applications on the top.
if (wtoken.hiddenRequested || wtoken.willBeHidden) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+ + " -- hidden on top");
continue;
}
haveGroup = true;
@@ -3243,6 +3261,8 @@
// user's orientation.
if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
&& lastFullscreen) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+ + " -- end of group, return " + lastOrientation);
return lastOrientation;
}
}
@@ -3253,16 +3273,21 @@
lastFullscreen = wtoken.appFullscreen;
if (lastFullscreen
&& or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+ + " -- full screen, return " + or);
return or;
}
// If this application has requested an explicit orientation,
// then use it.
if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
&& or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+ + " -- explicitly set, return " + or);
return or;
}
findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
}
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation");
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
@@ -3335,15 +3360,6 @@
* android.os.IBinder)
*/
boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
- if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
- // If the display is frozen, some activities may be in the middle
- // of restarting, and thus have removed their old window. If the
- // window has the flag to hide the lock screen, then the lock screen
- // can re-appear and inflict its own orientation on us. Keep the
- // orientation stable until this all settles down.
- return false;
- }
-
boolean changed = false;
long ident = Binder.clearCallingIdentity();
try {
@@ -8939,9 +8955,10 @@
pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig);
pw.print(" mRotation="); pw.print(mRotation);
- pw.print(" mForcedAppOrientation="); pw.print(mForcedAppOrientation);
pw.print(" mRequestedRotation="); pw.print(mRequestedRotation);
pw.print(" mAltOrientation="); pw.println(mAltOrientation);
+ pw.print(" mLastWindowForcedOrientation"); pw.print(mLastWindowForcedOrientation);
+ pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
pw.print(" mDeferredRotation="); pw.print(mDeferredRotation);
pw.print(", mDeferredRotationAnimFlags="); pw.println(mDeferredRotationAnimFlags);
pw.print(" mAnimationPending="); pw.print(mAnimationPending);
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 881882f1..7c5084f 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -56,7 +56,7 @@
// The exponent used to calculate the pointer speed scaling factor.
// The scaling factor is calculated as 2 ^ (speed * exponent),
// where the speed ranges from -7 to + 7 and is supplied by the user.
-static const float POINTER_SPEED_EXPONENT = 1.0f / 3;
+static const float POINTER_SPEED_EXPONENT = 1.0f / 4;
static struct {
jmethodID notifyConfigurationChanged;
@@ -76,10 +76,10 @@
jmethodID getKeyRepeatTimeout;
jmethodID getKeyRepeatDelay;
jmethodID getMaxEventsPerSecond;
- jmethodID getTapTimeout;
+ jmethodID getHoverTapTimeout;
+ jmethodID getHoverTapSlop;
jmethodID getDoubleTapTimeout;
jmethodID getLongPressTimeout;
- jmethodID getTouchSlop;
jmethodID getPointerLayer;
jmethodID getPointerIcon;
} gCallbacksClassInfo;
@@ -410,32 +410,32 @@
env->DeleteLocalRef(excludedDeviceNames);
}
- jint tapTimeout = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.getTapTimeout);
- if (!checkAndClearExceptionFromCallback(env, "getTapTimeout")) {
+ jint hoverTapTimeout = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.getHoverTapTimeout);
+ if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) {
jint doubleTapTimeout = env->CallIntMethod(mCallbacksObj,
gCallbacksClassInfo.getDoubleTapTimeout);
if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) {
jint longPressTimeout = env->CallIntMethod(mCallbacksObj,
gCallbacksClassInfo.getLongPressTimeout);
if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) {
- outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(tapTimeout);
+ outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(hoverTapTimeout);
// We must ensure that the tap-drag interval is significantly shorter than
// the long-press timeout because the tap is held down for the entire duration
// of the double-tap timeout.
jint tapDragInterval = max(min(longPressTimeout - 100,
- doubleTapTimeout), tapTimeout);
+ doubleTapTimeout), hoverTapTimeout);
outConfig->pointerGestureTapDragInterval =
milliseconds_to_nanoseconds(tapDragInterval);
}
}
}
- jint touchSlop = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.getTouchSlop);
- if (!checkAndClearExceptionFromCallback(env, "getTouchSlop")) {
- outConfig->pointerGestureTapSlop = touchSlop;
+ jint hoverTapSlop = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.getHoverTapSlop);
+ if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) {
+ outConfig->pointerGestureTapSlop = hoverTapSlop;
}
{ // acquire lock
@@ -1394,8 +1394,11 @@
GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, clazz,
"getKeyRepeatDelay", "()I");
- GET_METHOD_ID(gCallbacksClassInfo.getTapTimeout, clazz,
- "getTapTimeout", "()I");
+ GET_METHOD_ID(gCallbacksClassInfo.getHoverTapTimeout, clazz,
+ "getHoverTapTimeout", "()I");
+
+ GET_METHOD_ID(gCallbacksClassInfo.getHoverTapSlop, clazz,
+ "getHoverTapSlop", "()I");
GET_METHOD_ID(gCallbacksClassInfo.getDoubleTapTimeout, clazz,
"getDoubleTapTimeout", "()I");
@@ -1403,9 +1406,6 @@
GET_METHOD_ID(gCallbacksClassInfo.getLongPressTimeout, clazz,
"getLongPressTimeout", "()I");
- GET_METHOD_ID(gCallbacksClassInfo.getTouchSlop, clazz,
- "getTouchSlop", "()I");
-
GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz,
"getMaxEventsPerSecond", "()I");
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 3543275..5608402 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -16,6 +16,8 @@
package android.telephony;
+import android.util.Log;
+
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.gsm.SmsCbHeader;
@@ -58,10 +60,13 @@
try {
return new SmsCbMessage(pdu);
} catch (IllegalArgumentException e) {
+ Log.w(LOG_TAG, "Failed parsing SMS-CB pdu", e);
return null;
}
}
+ private static final String LOG_TAG = "SMSCB";
+
/**
* Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
*/
@@ -80,6 +85,8 @@
private static final char CARRIAGE_RETURN = 0x0d;
+ private static final int PDU_BODY_PAGE_LENGTH = 82;
+
private SmsCbHeader mHeader;
private String mLanguage;
@@ -149,6 +156,13 @@
return mHeader.updateNumber;
}
+ /**
+ * Parse and unpack the body text according to the encoding in the DCS.
+ * After completing successfully this method will have assigned the body
+ * text into mBody, and optionally the language code into mLanguage
+ *
+ * @param pdu The pdu
+ */
private void parseBody(byte[] pdu) {
int encoding;
boolean hasLanguageIndicator = false;
@@ -221,28 +235,81 @@
break;
}
+ if (mHeader.format == SmsCbHeader.FORMAT_UMTS) {
+ // Payload may contain multiple pages
+ int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
+
+ if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1)
+ * nrPages) {
+ throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match "
+ + nrPages + " pages");
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < nrPages; i++) {
+ // Each page is 82 bytes followed by a length octet indicating
+ // the number of useful octets within those 82
+ int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i;
+ int length = pdu[offset + PDU_BODY_PAGE_LENGTH];
+
+ if (length > PDU_BODY_PAGE_LENGTH) {
+ throw new IllegalArgumentException("Page length " + length
+ + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH);
+ }
+
+ sb.append(unpackBody(pdu, encoding, offset, length, hasLanguageIndicator));
+ }
+ mBody = sb.toString();
+ } else {
+ // Payload is one single page
+ int offset = SmsCbHeader.PDU_HEADER_LENGTH;
+ int length = pdu.length - offset;
+
+ mBody = unpackBody(pdu, encoding, offset, length, hasLanguageIndicator);
+ }
+ }
+
+ /**
+ * Unpack body text from the pdu using the given encoding, position and
+ * length within the pdu
+ *
+ * @param pdu The pdu
+ * @param encoding The encoding, as derived from the DCS
+ * @param offset Position of the first byte to unpack
+ * @param length Number of bytes to unpack
+ * @param hasLanguageIndicator true if the body text is preceded by a
+ * language indicator. If so, this method will as a side-effect
+ * assign the extracted language code into mLanguage
+ * @return Body text
+ */
+ private String unpackBody(byte[] pdu, int encoding, int offset, int length,
+ boolean hasLanguageIndicator) {
+ String body = null;
+
switch (encoding) {
case SmsMessage.ENCODING_7BIT:
- mBody = GsmAlphabet.gsm7BitPackedToString(pdu, SmsCbHeader.PDU_HEADER_LENGTH,
- (pdu.length - SmsCbHeader.PDU_HEADER_LENGTH) * 8 / 7);
+ body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7);
- if (hasLanguageIndicator && mBody != null && mBody.length() > 2) {
- mLanguage = mBody.substring(0, 2);
- mBody = mBody.substring(3);
+ if (hasLanguageIndicator && body != null && body.length() > 2) {
+ // Language is two GSM characters followed by a CR.
+ // The actual body text is offset by 3 characters.
+ mLanguage = body.substring(0, 2);
+ body = body.substring(3);
}
break;
case SmsMessage.ENCODING_16BIT:
- int offset = SmsCbHeader.PDU_HEADER_LENGTH;
-
- if (hasLanguageIndicator && pdu.length >= SmsCbHeader.PDU_HEADER_LENGTH + 2) {
- mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu,
- SmsCbHeader.PDU_HEADER_LENGTH, 2);
+ if (hasLanguageIndicator && pdu.length >= offset + 2) {
+ // Language is two GSM characters.
+ // The actual body text is offset by 2 bytes.
+ mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2);
offset += 2;
+ length -= 2;
}
try {
- mBody = new String(pdu, offset, (pdu.length & 0xfffe) - offset, "utf-16");
+ body = new String(pdu, offset, (length & 0xfffe), "utf-16");
} catch (UnsupportedEncodingException e) {
// Eeeek
}
@@ -252,16 +319,18 @@
break;
}
- if (mBody != null) {
+ if (body != null) {
// Remove trailing carriage return
- for (int i = mBody.length() - 1; i >= 0; i--) {
- if (mBody.charAt(i) != CARRIAGE_RETURN) {
- mBody = mBody.substring(0, i + 1);
+ for (int i = body.length() - 1; i >= 0; i--) {
+ if (body.charAt(i) != CARRIAGE_RETURN) {
+ body = body.substring(0, i + 1);
break;
}
}
} else {
- mBody = "";
+ body = "";
}
+
+ return body;
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8732e21..7272b08 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -469,6 +469,46 @@
}
}
+ /** Unknown network class. {@hide} */
+ public static final int NETWORK_CLASS_UNKNOWN = 0;
+ /** Class of broadly defined "2G" networks. {@hide} */
+ public static final int NETWORK_CLASS_2_G = 1;
+ /** Class of broadly defined "3G" networks. {@hide} */
+ public static final int NETWORK_CLASS_3_G = 2;
+ /** Class of broadly defined "4G" networks. {@hide} */
+ public static final int NETWORK_CLASS_4_G = 3;
+
+ /**
+ * Return general class of network type, such as "3G" or "4G". In cases
+ * where classification is contentious, this method is conservative.
+ *
+ * @hide
+ */
+ public static int getNetworkClass(int networkType) {
+ switch (networkType) {
+ case NETWORK_TYPE_GPRS:
+ case NETWORK_TYPE_EDGE:
+ case NETWORK_TYPE_CDMA:
+ case NETWORK_TYPE_1xRTT:
+ case NETWORK_TYPE_IDEN:
+ return NETWORK_CLASS_2_G;
+ case NETWORK_TYPE_UMTS:
+ case NETWORK_TYPE_EVDO_0:
+ case NETWORK_TYPE_EVDO_A:
+ case NETWORK_TYPE_HSDPA:
+ case NETWORK_TYPE_HSUPA:
+ case NETWORK_TYPE_HSPA:
+ case NETWORK_TYPE_EVDO_B:
+ case NETWORK_TYPE_EHRPD:
+ case NETWORK_TYPE_HSPAP:
+ return NETWORK_CLASS_3_G;
+ case NETWORK_TYPE_LTE:
+ return NETWORK_CLASS_4_G;
+ default:
+ return NETWORK_CLASS_UNKNOWN;
+ }
+ }
+
/**
* Returns a string representation of the radio technology (network type)
* currently in use on the device.
@@ -477,7 +517,12 @@
* @hide pending API council review
*/
public String getNetworkTypeName() {
- switch (getNetworkType()) {
+ return getNetworkTypeName(getNetworkType());
+ }
+
+ /** {@hide} */
+ public static String getNetworkTypeName(int type) {
+ switch (type) {
case NETWORK_TYPE_GPRS:
return "GPRS";
case NETWORK_TYPE_EDGE:
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index 0c4581b..8427d14 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -96,8 +96,10 @@
protected Registrant mRestrictedStateRegistrant;
protected Registrant mGsmBroadcastSmsRegistrant;
- // Network Mode received from PhoneFactory
- protected int mNetworkMode;
+ // Preferred network type received from PhoneFactory.
+ // This is used when establishing a connection to the
+ // vendor ril so it starts up in the correct mode.
+ protected int mPreferredNetworkType;
// CDMA subscription received from PhoneFactory
protected int mCdmaSubscription;
// Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone.
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 2c04b30..572bbaa 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -611,14 +611,14 @@
//***** Constructors
- public RIL(Context context, int networkMode, int cdmaSubscription) {
+ public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
super(context);
if (RILJ_LOGD) {
- riljLog("RIL(context, networkMode=" + networkMode +
+ riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType +
" cdmaSubscription=" + cdmaSubscription + ")");
}
mCdmaSubscription = cdmaSubscription;
- mNetworkMode = networkMode;
+ mPreferredNetworkType = preferredNetworkType;
mPhoneType = RILConstants.NO_PHONE;
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -1813,6 +1813,8 @@
rr.mp.writeInt(1);
rr.mp.writeInt(networkType);
+ mPreferredNetworkType = networkType;
+
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ " : " + networkType);
@@ -2222,7 +2224,7 @@
case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret = responseInts(p); break;
case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret = responseVoid(p); break;
case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret = responseVoid(p); break;
- case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseInts(p); break;
+ case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseGetPreferredNetworkType(p); break;
case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break;
case RIL_REQUEST_SET_LOCATION_UPDATES: ret = responseVoid(p); break;
case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret = responseVoid(p); break;
@@ -2737,7 +2739,7 @@
// Initial conditions
setRadioPower(false, null);
- setPreferredNetworkType(mNetworkMode, null);
+ setPreferredNetworkType(mPreferredNetworkType, null);
setCdmaSubscriptionSource(mCdmaSubscription, null);
notifyRegistrantsRilConnectionChanged(((int[])ret)[0]);
break;
@@ -3161,6 +3163,18 @@
return response;
}
+ private Object responseGetPreferredNetworkType(Parcel p) {
+ int [] response = (int[]) responseInts(p);
+
+ if (response.length >= 1) {
+ // Since this is the response for getPreferredNetworkType
+ // we'll assume that it should be the value we want the
+ // vendor ril to take if we reestablish a connection to it.
+ mPreferredNetworkType = response[0];
+ }
+ return response;
+ }
+
private Object responseGmsBroadcastConfig(Parcel p) {
int num;
ArrayList<SmsBroadcastConfigInfo> response;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 5f27cfc..0945a38 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -17,8 +17,31 @@
package com.android.internal.telephony.gsm;
public class SmsCbHeader {
+ /**
+ * Length of SMS-CB header
+ */
public static final int PDU_HEADER_LENGTH = 6;
+ /**
+ * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1
+ */
+ public static final int FORMAT_GSM = 1;
+
+ /**
+ * UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2
+ */
+ public static final int FORMAT_UMTS = 2;
+
+ /**
+ * Message type value as defined in 3gpp TS 25.324, section 11.1.
+ */
+ private static final int MESSAGE_TYPE_CBS_MESSAGE = 1;
+
+ /**
+ * Length of GSM pdus
+ */
+ private static final int PDU_LENGTH_GSM = 88;
+
public final int geographicalScope;
public final int messageCode;
@@ -33,27 +56,55 @@
public final int nrOfPages;
+ public final int format;
+
public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
throw new IllegalArgumentException("Illegal PDU");
}
- geographicalScope = (pdu[0] & 0xc0) >> 6;
- messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
- updateNumber = pdu[1] & 0x0f;
- messageIdentifier = (pdu[2] << 8) | pdu[3];
- dataCodingScheme = pdu[4];
+ if (pdu.length <= PDU_LENGTH_GSM) {
+ // GSM pdus are no more than 88 bytes
+ format = FORMAT_GSM;
+ geographicalScope = (pdu[0] & 0xc0) >> 6;
+ messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
+ updateNumber = pdu[1] & 0x0f;
+ messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff);
+ dataCodingScheme = pdu[4] & 0xff;
- // Check for invalid page parameter
- int pageIndex = (pdu[5] & 0xf0) >> 4;
- int nrOfPages = pdu[5] & 0x0f;
+ // Check for invalid page parameter
+ int pageIndex = (pdu[5] & 0xf0) >> 4;
+ int nrOfPages = pdu[5] & 0x0f;
- if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+ if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+ pageIndex = 1;
+ nrOfPages = 1;
+ }
+
+ this.pageIndex = pageIndex;
+ this.nrOfPages = nrOfPages;
+ } else {
+ // UMTS pdus are always at least 90 bytes since the payload includes
+ // a number-of-pages octet and also one length octet per page
+ format = FORMAT_UMTS;
+
+ int messageType = pdu[0];
+
+ if (messageType != MESSAGE_TYPE_CBS_MESSAGE) {
+ throw new IllegalArgumentException("Unsupported message type " + messageType);
+ }
+
+ messageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff;
+ geographicalScope = (pdu[3] & 0xc0) >> 6;
+ messageCode = ((pdu[3] & 0x3f) << 4) | ((pdu[4] & 0xf0) >> 4);
+ updateNumber = pdu[4] & 0x0f;
+ dataCodingScheme = pdu[5] & 0xff;
+
+ // We will always consider a UMTS message as having one single page
+ // since there's only one instance of the header, even though the
+ // actual payload may contain several pages.
pageIndex = 1;
nrOfPages = 1;
}
-
- this.pageIndex = pageIndex;
- this.nrOfPages = nrOfPages;
}
}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
index 7136ea0..b131a01 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
@@ -69,6 +69,36 @@
doTestGeographicalScopeValue(pdu, (byte)0xC0, SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE);
}
+ public void testGetGeographicalScopeUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+ (byte)0x01,
+
+ (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+ (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+ (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+ (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+ (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+ (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+ (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+ (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+ (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+ (byte)0x34
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected geographical scope decoded",
+ SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE, msg.getGeographicalScope());
+ }
+
public void testGetMessageBody7Bit() {
byte[] pdu = {
(byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -92,6 +122,83 @@
msg.getMessageBody());
}
+ public void testGetMessageBody7BitUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+ (byte)0x01,
+
+ (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+ (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+ (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+ (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+ (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+ (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+ (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+ (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+ (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+ (byte)0x34
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+ }
+
+ public void testGetMessageBody7BitMultipageUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x00, (byte)0x01, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+ (byte)0x02,
+
+ (byte)0xC6, (byte)0xB4, (byte)0x7C, (byte)0x4E, (byte)0x07, (byte)0xC1,
+ (byte)0xC3, (byte)0xE7, (byte)0xF2, (byte)0xAA, (byte)0xD1, (byte)0x68,
+ (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A,
+ (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34,
+ (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68,
+ (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+ (byte)0x0A,
+
+ (byte)0xD3, (byte)0xF2, (byte)0xF8, (byte)0xED, (byte)0x26, (byte)0x83,
+ (byte)0xE0, (byte)0xE1, (byte)0x73, (byte)0xB9, (byte)0xD1, (byte)0x68,
+ (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A,
+ (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34,
+ (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68,
+ (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+ (byte)0x0A
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected multipage 7-bit string decoded",
+ "First page+Second page",
+ msg.getMessageBody());
+ }
+
public void testGetMessageBody7BitFull() {
byte[] pdu = {
(byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -117,6 +224,38 @@
msg.getMessageBody());
}
+ public void testGetMessageBody7BitFullUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+ (byte)0x01,
+
+ (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+ (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+ (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+ (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+ (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xC4, (byte)0xE5, (byte)0xB4,
+ (byte)0xFB, (byte)0x0C, (byte)0x2A, (byte)0xE3, (byte)0xC3, (byte)0x63,
+ (byte)0x3A, (byte)0x3B, (byte)0x0F, (byte)0xCA, (byte)0xCD, (byte)0x40,
+ (byte)0x63, (byte)0x74, (byte)0x58, (byte)0x1E, (byte)0x1E, (byte)0xD3,
+ (byte)0xCB, (byte)0xF2, (byte)0x39, (byte)0x88, (byte)0xFD, (byte)0x76,
+ (byte)0x9F, (byte)0x59, (byte)0xA0, (byte)0x76, (byte)0x39, (byte)0xEC,
+ (byte)0x4E, (byte)0xBB, (byte)0xCF, (byte)0x20, (byte)0x3A, (byte)0xBA,
+ (byte)0x2C, (byte)0x2F, (byte)0x83, (byte)0xD2, (byte)0x73, (byte)0x90,
+ (byte)0xFB, (byte)0x0D, (byte)0x82, (byte)0x87, (byte)0xC9, (byte)0xE4,
+ (byte)0xB4, (byte)0xFB, (byte)0x1C, (byte)0x02,
+
+ (byte)0x52
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals(
+ "Unexpected 7-bit string decoded",
+ "A GSM default alphabet message being exactly 93 characters long, " +
+ "meaning there is no padding!",
+ msg.getMessageBody());
+ }
+
public void testGetMessageBody7BitWithLanguage() {
byte[] pdu = {
(byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x04, (byte)0x11, (byte)0x41,
@@ -167,6 +306,38 @@
assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
}
+ public void testGetMessageBody7BitWithLanguageInBodyUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x10,
+
+ (byte)0x01,
+
+ (byte)0x73, (byte)0x7B, (byte)0x23, (byte)0x08, (byte)0x3A, (byte)0x4E,
+ (byte)0x9B, (byte)0x20, (byte)0x72, (byte)0xD9, (byte)0x1C, (byte)0xAE,
+ (byte)0xB3, (byte)0xE9, (byte)0xA0, (byte)0x30, (byte)0x1B, (byte)0x8E,
+ (byte)0x0E, (byte)0x8B, (byte)0xCB, (byte)0x74, (byte)0x50, (byte)0xBB,
+ (byte)0x3C, (byte)0x9F, (byte)0x87, (byte)0xCF, (byte)0x65, (byte)0xD0,
+ (byte)0x3D, (byte)0x4D, (byte)0x47, (byte)0x83, (byte)0xC6, (byte)0x61,
+ (byte)0xB9, (byte)0x3C, (byte)0x1D, (byte)0x3E, (byte)0x97, (byte)0x41,
+ (byte)0xF2, (byte)0x32, (byte)0xBD, (byte)0x2E, (byte)0x77, (byte)0x83,
+ (byte)0xE0, (byte)0x61, (byte)0x32, (byte)0x39, (byte)0xED, (byte)0x3E,
+ (byte)0x37, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+ (byte)0x37
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
+ }
+
public void testGetMessageBody8Bit() {
byte[] pdu = {
(byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x44, (byte)0x11, (byte)0x41,
@@ -210,6 +381,81 @@
"A UCS2 message containing a \u0434 character", msg.getMessageBody());
}
+ public void testGetMessageBodyUcs2Umts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x48,
+
+ (byte)0x01,
+
+ (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55,
+ (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32,
+ (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x6D, (byte)0x00, (byte)0x65,
+ (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x61,
+ (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x20,
+ (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6E,
+ (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x69,
+ (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E,
+ (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61,
+ (byte)0x00, (byte)0x20, (byte)0x04, (byte)0x34, (byte)0x00, (byte)0x20,
+ (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68, (byte)0x00, (byte)0x61,
+ (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x63,
+ (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x72,
+ (byte)0x00, (byte)0x0D, (byte)0x00, (byte)0x0D,
+
+ (byte)0x4E
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+ }
+
+ public void testGetMessageBodyUcs2MultipageUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x48,
+
+ (byte)0x02,
+
+ (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x41,
+ (byte)0x00, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+
+ (byte)0x06,
+
+ (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x42,
+ (byte)0x00, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+ (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+
+ (byte)0x06
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected multipage UCS2 string decoded",
+ "AAABBB", msg.getMessageBody());
+ }
+
public void testGetMessageBodyUcs2WithLanguageInBody() {
byte[] pdu = {
(byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x11, (byte)0x11, (byte)0x78,
@@ -234,6 +480,37 @@
assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
}
+ public void testGetMessageBodyUcs2WithLanguageInBodyUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x11,
+
+ (byte)0x01,
+
+ (byte)0x78, (byte)0x3C, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20,
+ (byte)0x00, (byte)0x55, (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53,
+ (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x6D,
+ (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73,
+ (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65,
+ (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F,
+ (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61,
+ (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69,
+ (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20,
+ (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x20, (byte)0x04, (byte)0x34,
+ (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68,
+ (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61,
+ (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65,
+ (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x0D,
+
+ (byte)0x50
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
+ }
+
public void testGetMessageIdentifier() {
byte[] pdu = {
(byte)0xC0, (byte)0x00, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -256,6 +533,35 @@
assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
}
+ public void testGetMessageIdentifierUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x30, (byte)0x39, (byte)0x2A, (byte)0xA5, (byte)0x40,
+
+ (byte)0x01,
+
+ (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+ (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+ (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+ (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+ (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+ (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+ (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+ (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+ (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+ (byte)0x34
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
+ }
+
public void testGetMessageCode() {
byte[] pdu = {
(byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -278,6 +584,35 @@
assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
}
+ public void testGetMessageCodeUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x30, (byte)0x39, (byte)0x2A, (byte)0xA5, (byte)0x40,
+
+ (byte)0x01,
+
+ (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+ (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+ (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+ (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+ (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+ (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+ (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+ (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+ (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+ (byte)0x34
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
+ }
+
public void testGetUpdateNumber() {
byte[] pdu = {
(byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -299,4 +634,33 @@
assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
}
+
+ public void testGetUpdateNumberUmts() {
+ byte[] pdu = {
+ (byte)0x01, (byte)0x30, (byte)0x39, (byte)0x2A, (byte)0xA5, (byte)0x40,
+
+ (byte)0x01,
+
+ (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+ (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+ (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+ (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+ (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+ (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+ (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+ (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+ (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+ (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+ (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+ (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+ (byte)0x34
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
+ }
}
diff --git a/tests/BiDiTests/res/layout/frame_layout_ltr.xml b/tests/BiDiTests/res/layout/frame_layout_ltr.xml
index bd324e3..61fd06e 100644
--- a/tests/BiDiTests/res/layout/frame_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/frame_layout_ltr.xml
@@ -18,7 +18,7 @@
android:id="@+id/frame_layout_ltr"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="ltr"
+ android:layoutDirection="ltr"
android:background="#FF000000">
<FrameLayout
@@ -52,28 +52,28 @@
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="top|before"
+ android:layout_gravity="top|start"
android:background="#FFFFFFFF">
</FrameLayout>
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="top|after"
+ android:layout_gravity="top|end"
android:background="#FFFFFF00">
</FrameLayout>
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="bottom|before"
+ android:layout_gravity="bottom|start"
android:background="#FFFFFFFF">
</FrameLayout>
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="bottom|after"
+ android:layout_gravity="bottom|end"
android:background="#FFFFFF00">
</FrameLayout>
diff --git a/tests/BiDiTests/res/layout/frame_layout_rtl.xml b/tests/BiDiTests/res/layout/frame_layout_rtl.xml
index 814f5e1..598b41a 100644
--- a/tests/BiDiTests/res/layout/frame_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/frame_layout_rtl.xml
@@ -18,7 +18,7 @@
android:id="@+id/frame_layout_ltr"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="rtl"
+ android:layoutDirection="rtl"
android:background="#FF000000">
<FrameLayout
@@ -52,28 +52,28 @@
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="top|before"
+ android:layout_gravity="top|start"
android:background="#FFFFFFFF">
</FrameLayout>
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="top|after"
+ android:layout_gravity="top|end"
android:background="#FFFFFF00">
</FrameLayout>
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="bottom|before"
+ android:layout_gravity="bottom|start"
android:background="#FFFFFFFF">
</FrameLayout>
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="bottom|after"
+ android:layout_gravity="bottom|end"
android:background="#FFFFFF00">
</FrameLayout>
diff --git a/tests/BiDiTests/res/layout/linear_layout_ltr.xml b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
index c5a8d47..d4386f2 100644
--- a/tests/BiDiTests/res/layout/linear_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
@@ -19,7 +19,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
@@ -51,7 +51,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="inherit">
+ android:layoutDirection="inherit">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -78,7 +78,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -105,7 +105,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -132,7 +132,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="inherit">
+ android:layoutDirection="inherit">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -159,7 +159,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -186,7 +186,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
diff --git a/tests/BiDiTests/res/layout/linear_layout_rtl.xml b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
index 1494fec..9d07263 100644
--- a/tests/BiDiTests/res/layout/linear_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
@@ -19,7 +19,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
@@ -51,7 +51,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="inherit">
+ android:layoutDirection="inherit">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -78,7 +78,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -105,7 +105,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -132,7 +132,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="inherit">
+ android:layoutDirection="inherit">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -159,7 +159,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
@@ -186,7 +186,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<Button android:layout_height="wrap_content"
android:layout_width="wrap_content"
diff --git a/tests/BiDiTests/res/layout/relative_layout_ltr.xml b/tests/BiDiTests/res/layout/relative_layout_ltr.xml
index 53ae7c3..d789707 100644
--- a/tests/BiDiTests/res/layout/relative_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_ltr.xml
@@ -18,7 +18,7 @@
android:id="@+id/frame_layout_ltr"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="ltr"
+ android:layoutDirection="ltr"
android:background="#FF000000">
<RelativeLayout
@@ -52,28 +52,28 @@
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="top|before"
+ android:layout_gravity="top|start"
android:background="#FFFFFFFF">
</RelativeLayout>
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="top|after"
+ android:layout_gravity="top|end"
android:background="#FFFFFF00">
</RelativeLayout>
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="bottom|before"
+ android:layout_gravity="bottom|start"
android:background="#FFFFFFFF">
</RelativeLayout>
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="bottom|after"
+ android:layout_gravity="bottom|end"
android:background="#FFFFFF00">
</RelativeLayout>
diff --git a/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml b/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml
index 93d1b4b..a13ef8b 100644
--- a/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml
@@ -19,7 +19,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
@@ -58,7 +58,7 @@
android:layout_height="wrap_content"
android:background="#FF000000"
android:padding="10px"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<TextView android:id="@+id/label_2"
android:layout_width="fill_parent"
@@ -91,7 +91,7 @@
android:layout_height="wrap_content"
android:background="#FF000000"
android:padding="10px"
- android:horizontalDirection="inherit">
+ android:layoutDirection="inherit">
<TextView android:id="@+id/label_3"
android:layout_width="fill_parent"
@@ -124,7 +124,7 @@
android:layout_height="wrap_content"
android:background="#FF000000"
android:padding="10px"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<TextView android:id="@+id/label_4"
android:layout_width="fill_parent"
diff --git a/tests/BiDiTests/res/layout/relative_layout_rtl.xml b/tests/BiDiTests/res/layout/relative_layout_rtl.xml
index 4b87752..5808924 100644
--- a/tests/BiDiTests/res/layout/relative_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_rtl.xml
@@ -18,7 +18,7 @@
android:id="@+id/frame_layout_rtl"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="rtl"
+ android:layoutDirection="rtl"
android:background="#FF000000">
<RelativeLayout
@@ -52,28 +52,28 @@
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="top|before"
+ android:layout_gravity="top|start"
android:background="#FFFFFFFF">
</RelativeLayout>
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="top|after"
+ android:layout_gravity="top|end"
android:background="#FFFFFF00">
</RelativeLayout>
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="bottom|before"
+ android:layout_gravity="bottom|start"
android:background="#FFFFFFFF">
</RelativeLayout>
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
- android:layout_gravity="bottom|after"
+ android:layout_gravity="bottom|end"
android:background="#FFFFFF00">
</RelativeLayout>
diff --git a/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml b/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml
index 8418e72..1a6b3d5 100644
--- a/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml
@@ -19,7 +19,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
@@ -58,7 +58,7 @@
android:layout_height="wrap_content"
android:background="#FF000000"
android:padding="10px"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<TextView android:id="@+id/label_2"
android:layout_width="fill_parent"
@@ -91,7 +91,7 @@
android:layout_height="wrap_content"
android:background="#FF000000"
android:padding="10px"
- android:horizontalDirection="inherit">
+ android:layoutDirection="inherit">
<TextView android:id="@+id/label_3"
android:layout_width="fill_parent"
@@ -124,7 +124,7 @@
android:layout_height="wrap_content"
android:background="#FF000000"
android:padding="10px"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<TextView android:id="@+id/label_4"
android:layout_width="fill_parent"
diff --git a/tests/BiDiTests/res/layout/table_layout_ltr.xml b/tests/BiDiTests/res/layout/table_layout_ltr.xml
index f44de8e..8e1891e 100644
--- a/tests/BiDiTests/res/layout/table_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/table_layout_ltr.xml
@@ -19,7 +19,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
@@ -72,7 +72,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="1,2"
- android:horizontalDirection="inherit">
+ android:layoutDirection="inherit">
<TableRow>
<Button android:layout_height="wrap_content"
@@ -120,7 +120,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="1,2"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<TableRow>
<Button android:layout_height="wrap_content"
@@ -168,7 +168,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="1,2"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<TableRow>
<Button android:layout_height="wrap_content"
diff --git a/tests/BiDiTests/res/layout/table_layout_rtl.xml b/tests/BiDiTests/res/layout/table_layout_rtl.xml
index 84270ba..bd664e4 100644
--- a/tests/BiDiTests/res/layout/table_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/table_layout_rtl.xml
@@ -19,7 +19,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
@@ -72,7 +72,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="1,2"
- android:horizontalDirection="inherit">
+ android:layoutDirection="inherit">
<TableRow>
<Button android:layout_height="wrap_content"
@@ -120,7 +120,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="1,2"
- android:horizontalDirection="ltr">
+ android:layoutDirection="ltr">
<TableRow>
<Button android:layout_height="wrap_content"
@@ -168,7 +168,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="1,2"
- android:horizontalDirection="rtl">
+ android:layoutDirection="rtl">
<TableRow>
<Button android:layout_height="wrap_content"
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index 2aeb9e1..d067e7d 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -19,9 +19,9 @@
<string name="button3_text">Button3</string>
<string name="button4_text">Button4</string>
<string name="button_right_text">Right</string>
- <string name="button_after_text">After</string>
+ <string name="button_after_text">End</string>
<string name="button_left_text">Left</string>
- <string name="button_before_text">Before</string>
+ <string name="button_before_text">Start</string>
<string name="button_requestlayout_text">Request Layout</string>
<string name="textview_text">This is a text for a TextView</string>
<string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/globe.png b/tests/RenderScriptTests/PerfTest/res/drawable/globe.png
new file mode 100755
index 0000000..f9d6172
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/res/drawable/globe.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
index 5377f12..3ba6ba4 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
@@ -113,16 +113,20 @@
private Allocation mTexOpaque;
private Allocation mTexTransparent;
private Allocation mTexChecker;
+ private Allocation mTexGlobe;
private Mesh m10by10Mesh;
private Mesh m100by100Mesh;
private Mesh mWbyHMesh;
private Mesh mTorus;
+ private Mesh mSingleMesh;
Font mFontSans;
Font mFontSerif;
private Allocation mTextAlloc;
+ private ScriptField_TexAllocs_s mTextureAllocs;
+
private ScriptC_rsbench mScript;
private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
@@ -253,6 +257,27 @@
return tmb.create(true);
}
+ /**
+ * Create a mesh with a single quad for the given width and height.
+ */
+ private Mesh getSingleMesh(float width, float height) {
+ Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
+ 2, Mesh.TriangleMeshBuilder.TEXTURE_0);
+ float xOffset = width/2;
+ float yOffset = height/2;
+ tmb.setTexture(0, 0);
+ tmb.addVertex(-1.0f * xOffset, -1.0f * yOffset);
+ tmb.setTexture(1, 0);
+ tmb.addVertex(xOffset, -1.0f * yOffset);
+ tmb.setTexture(1, 1);
+ tmb.addVertex(xOffset, yOffset);
+ tmb.setTexture(0, 1);
+ tmb.addVertex(-1.0f * xOffset, yOffset);
+ tmb.addTriangle(0, 3, 1);
+ tmb.addTriangle(1, 3, 2);
+ return tmb.create(true);
+ }
+
private void initProgramStore() {
// Use stock the stock program store object
mProgStoreBlendNoneDepth = ProgramStore.BLEND_NONE_DEPTH_TEST(mRS);
@@ -392,11 +417,13 @@
mTexOpaque = loadTextureRGB(R.drawable.data);
mTexTransparent = loadTextureARGB(R.drawable.leaf);
mTexChecker = loadTextureRGB(R.drawable.checker);
+ mTexGlobe = loadTextureRGB(R.drawable.globe);
mScript.set_gTexTorus(mTexTorus);
mScript.set_gTexOpaque(mTexOpaque);
mScript.set_gTexTransparent(mTexTransparent);
mScript.set_gTexChecker(mTexChecker);
+ mScript.set_gTexGlobe(mTexGlobe);
}
private void initFonts() {
@@ -418,6 +445,8 @@
mScript.set_g100by100Mesh(m100by100Mesh);
mWbyHMesh= getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, mBenchmarkDimX/4, mBenchmarkDimY/4);
mScript.set_gWbyHMesh(mWbyHMesh);
+ mSingleMesh = getSingleMesh(50, 50);
+ mScript.set_gSingleMesh(mSingleMesh);
FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
FileA3D.IndexEntry entry = model.getIndexEntry(0);
@@ -517,6 +546,16 @@
Allocation.USAGE_GRAPHICS_RENDER_TARGET);
mScript.set_gRenderBufferDepth(offscreen);
+
+ mTextureAllocs = new ScriptField_TexAllocs_s(mRS, 100);
+ for (int i = 0; i < 100; i++) {
+ ScriptField_TexAllocs_s.Item texElem = new ScriptField_TexAllocs_s.Item();
+ texElem.texture = loadTextureRGB(R.drawable.globe);
+ mTextureAllocs.set(texElem, i, false);
+ }
+ mTextureAllocs.copyAll();
+ mScript.bind_gTexList100(mTextureAllocs);
+
mRS.bindRootScript(mScript);
}
}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
index e7f5cd4..b6572fb 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
@@ -23,7 +23,7 @@
const int RS_MSG_TEST_DONE = 100;
const int RS_MSG_RESULTS_READY = 101;
-const int gMaxModes = 25;
+const int gMaxModes = 27;
int gMaxLoops;
// Allocation to send test names back to java
@@ -44,11 +44,19 @@
rs_allocation gTexTorus;
rs_allocation gTexTransparent;
rs_allocation gTexChecker;
+rs_allocation gTexGlobe;
+
+typedef struct TexAllocs_s {
+ rs_allocation texture;
+} TexAllocs;
+
+TexAllocs *gTexList100;
rs_mesh g10by10Mesh;
rs_mesh g100by100Mesh;
rs_mesh gWbyHMesh;
rs_mesh gTorusMesh;
+rs_mesh gSingleMesh;
rs_font gFontSans;
rs_font gFontSerif;
@@ -142,7 +150,7 @@
}
static void bindProgramVertexOrtho() {
- // Default vertex sahder
+ // Default vertex shader
rsgBindProgramVertex(gProgVertex);
// Setup the projection matrix
rs_matrix4x4 proj;
@@ -198,6 +206,33 @@
}
}
+// Display sample images in a mesh with different texture
+static void displayMeshWithMultiTexture(int meshMode) {
+ bindProgramVertexOrtho();
+
+ // Fragment shader with texture
+ rsgBindProgramStore(gProgStoreBlendAlpha);
+ rsgBindProgramFragment(gProgFragmentTexture);
+ rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+
+ int meshCount = (int)pow(10.0f, (float)(meshMode + 1));
+
+ float yPos = 0;
+ for (int y = 0; y < meshCount; y++) {
+ yPos = (y + 1) * 50;
+ float xPos = 0;
+ for (int x = 0; x < meshCount; x++) {
+ xPos = (x + 1) * 50;
+ rs_matrix4x4 matrix;
+ rsMatrixLoadTranslate(&matrix, xPos, yPos, 0);
+ rsgProgramVertexLoadModelMatrix(&matrix);
+ int i = (x + y * meshCount) % 100;
+ rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].texture);
+ rsgDrawMesh(gSingleMesh);
+ }
+ }
+}
+
static float gTorusRotation = 0;
static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) {
if (buffer == 0) {
@@ -497,6 +532,8 @@
"Geo test 25.6k heavy fragment heavy vertex",
"Geo test 51.2k heavy fragment heavy vertex",
"Geo test 204.8k small tries heavy fragment heavy vertex",
+ "Mesh with 10 by 10 texture",
+ "Mesh with 100 by 100 texture",
};
void getTestName(int testIndex) {
@@ -589,6 +626,12 @@
case 24:
displayPixelLightSamples(8, true);
break;
+ case 25:
+ displayMeshWithMultiTexture(0);
+ break;
+ case 26:
+ displayMeshWithMultiTexture(1);
+ break;
}
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 922cd4c..8e3ed93 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -425,7 +425,7 @@
AffineTransform matrixTx = matrixDelegate.getAffineTransform();
// combine them so that the given matrix is applied after.
- currentTx.preConcatenate(matrixTx);
+ currentTx.concatenate(matrixTx);
// give it to the graphics2D as a new matrix replacing all previous transform
snapshot.setTransform(currentTx);
@@ -717,7 +717,7 @@
/*package*/ static void native_drawCircle(int nativeCanvas,
float cx, float cy, float radius, int paint) {
native_drawOval(nativeCanvas,
- new RectF(cx - radius, cy - radius, radius*2, radius*2),
+ new RectF(cx - radius, cy - radius, radius, radius),
paint);
}
diff --git a/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java
new file mode 100644
index 0000000..afbe97c0
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Delegate overriding selected methods of android.os.HandlerThread
+ *
+ * Through the layoutlib_create tool, selected methods of Handler have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ *
+ */
+public class HandlerThread_Delegate {
+
+ private static Map<BridgeContext, List<HandlerThread>> sThreads =
+ new HashMap<BridgeContext, List<HandlerThread>>();
+
+ public static void cleanUp(BridgeContext context) {
+ List<HandlerThread> list = sThreads.get(context);
+ if (list != null) {
+ for (HandlerThread thread : list) {
+ thread.quit();
+ }
+
+ list.clear();
+ sThreads.remove(context);
+ }
+ }
+
+ // -------- Delegate methods
+
+ @LayoutlibDelegate
+ /*package*/ static void run(HandlerThread theThread) {
+ // record the thread so that it can be quit() on clean up.
+ BridgeContext context = RenderAction.getCurrentContext();
+ List<HandlerThread> list = sThreads.get(context);
+ if (list == null) {
+ list = new ArrayList<HandlerThread>();
+ sThreads.put(context, list);
+ }
+
+ list.add(theThread);
+
+ // ---- START DEFAULT IMPLEMENTATION.
+
+ theThread.mTid = Process.myTid();
+ Looper.prepare();
+ synchronized (theThread) {
+ theThread.mLooper = Looper.myLooper();
+ theThread.notifyAll();
+ }
+ Process.setThreadPriority(theThread.mPriority);
+ theThread.onLooperPrepared();
+ Looper.loop();
+ theThread.mTid = -1;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 0f3cf57..3ef3288 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -22,7 +22,10 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
+import android.util.Xml;
import java.io.IOException;
@@ -35,6 +38,8 @@
*/
public class LayoutInflater_Delegate {
+ public static boolean sIsInInclude = false;
+
/**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
@@ -94,4 +99,128 @@
}
}
}
+
+ @LayoutlibDelegate
+ public static void parseInclude(
+ LayoutInflater thisInflater,
+ XmlPullParser parser, View parent, AttributeSet attrs)
+ throws XmlPullParserException, IOException {
+
+ int type;
+
+ if (parent instanceof ViewGroup) {
+ final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
+ if (layout == 0) {
+ final String value = attrs.getAttributeValue(null, "layout");
+ if (value == null) {
+ throw new InflateException("You must specifiy a layout in the"
+ + " include tag: <include layout=\"@layout/layoutID\" />");
+ } else {
+ throw new InflateException("You must specifiy a valid layout "
+ + "reference. The layout ID " + value + " is not valid.");
+ }
+ } else {
+ final XmlResourceParser childParser =
+ thisInflater.getContext().getResources().getLayout(layout);
+
+ try {
+ final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
+
+ while ((type = childParser.next()) != XmlPullParser.START_TAG &&
+ type != XmlPullParser.END_DOCUMENT) {
+ // Empty.
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new InflateException(childParser.getPositionDescription() +
+ ": No start tag found!");
+ }
+
+ final String childName = childParser.getName();
+
+ if (LayoutInflater.TAG_MERGE.equals(childName)) {
+ // Inflate all children.
+ thisInflater.rInflate(childParser, parent, childAttrs, false);
+ } else {
+ final View view = thisInflater.createViewFromTag(parent, childName, childAttrs);
+ final ViewGroup group = (ViewGroup) parent;
+
+ // We try to load the layout params set in the <include /> tag. If
+ // they don't exist, we will rely on the layout params set in the
+ // included XML file.
+ // During a layoutparams generation, a runtime exception is thrown
+ // if either layout_width or layout_height is missing. We catch
+ // this exception and set localParams accordingly: true means we
+ // successfully loaded layout params from the <include /> tag,
+ // false means we need to rely on the included layout params.
+ ViewGroup.LayoutParams params = null;
+ try {
+ // ---- START CHANGES
+ sIsInInclude = true;
+ // ---- END CHANGES
+
+ params = group.generateLayoutParams(attrs);
+
+ } catch (RuntimeException e) {
+ // ---- START CHANGES
+ sIsInInclude = false;
+ // ---- END CHANGES
+
+ params = group.generateLayoutParams(childAttrs);
+ } finally {
+ // ---- START CHANGES
+ sIsInInclude = false;
+ // ---- END CHANGES
+
+ if (params != null) {
+ view.setLayoutParams(params);
+ }
+ }
+
+ // Inflate all children.
+ thisInflater.rInflate(childParser, view, childAttrs, true);
+
+ // Attempt to override the included layout's android:id with the
+ // one set on the <include /> tag itself.
+ TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.View, 0, 0);
+ int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
+ // While we're at it, let's try to override android:visibility.
+ int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
+ a.recycle();
+
+ if (id != View.NO_ID) {
+ view.setId(id);
+ }
+
+ switch (visibility) {
+ case 0:
+ view.setVisibility(View.VISIBLE);
+ break;
+ case 1:
+ view.setVisibility(View.INVISIBLE);
+ break;
+ case 2:
+ view.setVisibility(View.GONE);
+ break;
+ }
+
+ group.addView(view);
+ }
+ } finally {
+ childParser.close();
+ }
+ }
+ } else {
+ throw new InflateException("<include /> can only be used inside of a ViewGroup");
+ }
+
+ final int currentDepth = parser.getDepth();
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
+ // Empty
+ }
+ }
+
+
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 144ec42..3ba3257 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -196,7 +196,7 @@
Capability.UNBOUND_RENDERING,
Capability.CUSTOM_BACKGROUND_COLOR,
Capability.RENDER,
- //Capability.LAYOUT_ONLY, // disable to run on ADT 10.0 which doesn't include this.
+ Capability.LAYOUT_ONLY,
Capability.EMBEDDED_LAYOUT,
Capability.VIEW_MANIPULATION,
Capability.PLAY_ANIMATION,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index e536028..4fa924d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -71,7 +71,6 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
@@ -342,7 +341,7 @@
try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(xml));
+ parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
// set the resource ref to have correct view cookies
mBridgeInflater.setResourceReference(resource);
@@ -514,14 +513,13 @@
BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
isPlatformFile);
- // resolve the defStyleAttr value into a IStyleResourceValue
- StyleResourceValue defStyleValues = null;
-
// look for a custom style.
String customStyle = null;
if (set != null) {
customStyle = set.getAttributeValue(null /* namespace*/, "style");
}
+
+ StyleResourceValue customStyleValues = null;
if (customStyle != null) {
ResourceValue item = mRenderResources.findResValue(customStyle,
false /*forceFrameworkOnly*/);
@@ -530,75 +528,76 @@
item = mRenderResources.resolveResValue(item);
if (item instanceof StyleResourceValue) {
- defStyleValues = (StyleResourceValue)item;
+ customStyleValues = (StyleResourceValue)item;
}
}
- if (defStyleValues == null) {
- if (defStyleAttr != 0) {
- // get the name from the int.
- String defStyleName = searchAttr(defStyleAttr);
+ // resolve the defStyleAttr value into a IStyleResourceValue
+ StyleResourceValue defStyleValues = null;
- if (defaultPropMap != null) {
- defaultPropMap.put("style", defStyleName);
+ if (defStyleAttr != 0) {
+ // get the name from the int.
+ String defStyleName = searchAttr(defStyleAttr);
+
+ if (defaultPropMap != null) {
+ defaultPropMap.put("style", defStyleName);
+ }
+
+ // look for the style in the current theme, and its parent:
+ ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
+
+ if (item != null) {
+ // item is a reference to a style entry. Search for it.
+ item = mRenderResources.findResValue(item.getValue(),
+ false /*forceFrameworkOnly*/);
+
+ if (item instanceof StyleResourceValue) {
+ defStyleValues = (StyleResourceValue)item;
}
+ } else {
+ Bridge.getLog().error(null,
+ String.format(
+ "Failed to find style '%s' in current theme", defStyleName),
+ null /*data*/);
+ }
+ } else if (defStyleRes != 0) {
+ Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
+ if (value == null) {
+ value = mProjectCallback.resolveResourceId(defStyleRes);
+ }
- // look for the style in the current theme, and its parent:
- ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
-
- if (item != null) {
- // item is a reference to a style entry. Search for it.
- item = mRenderResources.findResValue(item.getValue(),
- false /*forceFrameworkOnly*/);
-
- if (item instanceof StyleResourceValue) {
- defStyleValues = (StyleResourceValue)item;
- }
- } else {
- Bridge.getLog().error(null,
- String.format(
- "Failed to find style '%s' in current theme", defStyleName),
- null /*data*/);
- }
- } else if (defStyleRes != 0) {
- Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
- if (value == null) {
- value = mProjectCallback.resolveResourceId(defStyleRes);
- }
-
- if (value != null) {
- if (value.getFirst() == ResourceType.STYLE) {
- // look for the style in the current theme, and its parent:
- ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
- if (item != null) {
- if (item instanceof StyleResourceValue) {
- if (defaultPropMap != null) {
- defaultPropMap.put("style", item.getName());
- }
-
- defStyleValues = (StyleResourceValue)item;
+ if (value != null) {
+ if (value.getFirst() == ResourceType.STYLE) {
+ // look for the style in the current theme, and its parent:
+ ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
+ if (item != null) {
+ if (item instanceof StyleResourceValue) {
+ if (defaultPropMap != null) {
+ defaultPropMap.put("style", item.getName());
}
- } else {
- Bridge.getLog().error(null,
- String.format(
- "Style with id 0x%x (resolved to '%s') does not exist.",
- defStyleRes, value.getSecond()),
- null /*data*/);
+
+ defStyleValues = (StyleResourceValue)item;
}
} else {
Bridge.getLog().error(null,
String.format(
- "Resouce id 0x%x is not of type STYLE (instead %s)",
- defStyleRes, value.getFirst().toString()),
+ "Style with id 0x%x (resolved to '%s') does not exist.",
+ defStyleRes, value.getSecond()),
null /*data*/);
}
} else {
Bridge.getLog().error(null,
String.format(
- "Failed to find style with id 0x%x in current theme",
- defStyleRes),
+ "Resouce id 0x%x is not of type STYLE (instead %s)",
+ defStyleRes, value.getFirst().toString()),
null /*data*/);
}
+ } else {
+ Bridge.getLog().error(null,
+ String.format(
+ "Failed to find style with id 0x%x in current theme",
+ defStyleRes),
+ null /*data*/);
}
}
@@ -623,8 +622,13 @@
if (value == null) {
ResourceValue resValue = null;
- // look for the value in the defStyle first (and its parent if needed)
- if (defStyleValues != null) {
+ // look for the value in the custom style first (and its parent if needed)
+ if (customStyleValues != null) {
+ resValue = mRenderResources.findItemInStyle(customStyleValues, name);
+ }
+
+ // then look for the value in the default Style (and its parent if needed)
+ if (resValue == null && defStyleValues != null) {
resValue = mRenderResources.findItemInStyle(defStyleValues, name);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
index d6bbebd..7c90a31 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
@@ -36,7 +36,7 @@
import android.view.ViewGroup;
import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
/**
* Custom implementation of {@link LayoutInflater} to handle custom views.
@@ -177,7 +177,7 @@
try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
parser, bridgeContext, false);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
index 273e493..345f053 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
@@ -46,7 +46,6 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.InputStream;
/**
@@ -244,7 +243,7 @@
// give that to our XmlBlockParser
parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(xml));
+ parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
}
}
@@ -282,7 +281,7 @@
// give that to our XmlBlockParser
parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(xml));
+ parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
}
@@ -501,7 +500,7 @@
try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
} catch (XmlPullParserException e) {
@@ -536,7 +535,7 @@
try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
} catch (XmlPullParserException e) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index b9f769f..d4600a1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -36,10 +36,11 @@
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import android.view.LayoutInflater_Delegate;
import android.view.ViewGroup.LayoutParams;
import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
import java.util.Arrays;
import java.util.Map;
@@ -315,7 +316,7 @@
try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, mContext, resValue.isFramework());
@@ -471,40 +472,23 @@
*/
@Override
public int getDimensionPixelSize(int index, int defValue) {
- if (mResourceData[index] == null) {
+ try {
+ return getDimension(index);
+ } catch (RuntimeException e) {
+ if (mResourceData[index] != null) {
+ String s = mResourceData[index].getValue();
+
+ if (s != null) {
+ // looks like we were unable to resolve the dimension value
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+ s, mNames[index]), null /*data*/);
+ }
+ }
+
return defValue;
}
-
- String s = mResourceData[index].getValue();
-
- if (s == null) {
- return defValue;
- } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
- s.equals(BridgeConstants.FILL_PARENT)) {
- return LayoutParams.MATCH_PARENT;
- } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
- return LayoutParams.WRAP_CONTENT;
- } else if (RenderResources.REFERENCE_NULL.equals(s)) {
- return defValue;
- }
-
- if (ResourceHelper.stringToFloat(s, mValue)) {
- float f = mValue.getDimension(mBridgeResources.mMetrics);
-
- final int res = (int)(f+0.5f);
- if (res != 0) return res;
- if (f == 0) return 0;
- if (f > 0) return 1;
- return defValue; // this is basically unreachable.
- }
-
- // looks like we were unable to resolve the dimension value
- Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
- String.format(
- "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
- s, mNames[index]), null /*data*/);
-
- return defValue;
}
/**
@@ -521,7 +505,20 @@
*/
@Override
public int getLayoutDimension(int index, String name) {
- return getDimensionPixelSize(index, 0);
+ try {
+ // this will throw an exception
+ return getDimension(index);
+ } catch (RuntimeException e) {
+
+ if (LayoutInflater_Delegate.sIsInInclude) {
+ throw new RuntimeException();
+ }
+
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ "You must supply a " + name + " attribute.", null);
+
+ return 0;
+ }
}
@Override
@@ -529,6 +526,36 @@
return getDimensionPixelSize(index, defValue);
}
+ private int getDimension(int index) {
+ if (mResourceData[index] == null) {
+ throw new RuntimeException();
+ }
+
+ String s = mResourceData[index].getValue();
+
+ if (s == null) {
+ throw new RuntimeException();
+ } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
+ s.equals(BridgeConstants.FILL_PARENT)) {
+ return LayoutParams.MATCH_PARENT;
+ } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+ return LayoutParams.WRAP_CONTENT;
+ } else if (RenderResources.REFERENCE_NULL.equals(s)) {
+ throw new RuntimeException();
+ }
+
+ if (ResourceHelper.stringToFloat(s, mValue)) {
+ float f = mValue.getDimension(mBridgeResources.mMetrics);
+
+ final int res = (int)(f+0.5f);
+ if (res != 0) return res;
+ if (f == 0) return 0;
+ if (f > 0) return 1;
+ }
+
+ throw new RuntimeException();
+ }
+
/**
* Retrieve a fractional unit attribute at <var>index</var>.
*
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 0c4b0d3..060e6ee 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -73,7 +73,7 @@
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(
getClass().getResourceAsStream(layoutPath),
- "UTF8");
+ "UTF8"); //$NON-NLS-1$
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
parser, (BridgeContext) context, false /*platformFile*/);
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 8e80c21..6194f5d 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
@@ -29,6 +29,7 @@
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.ResourceType;
+import android.os.HandlerThread_Delegate;
import android.util.DisplayMetrics;
import java.util.concurrent.TimeUnit;
@@ -228,6 +229,10 @@
private void tearDown() {
// Make sure to remove static references, otherwise we could not unload the lib
mContext.disposeResources();
+
+ // quit HandlerThread created during this session.
+ HandlerThread_Delegate.cleanUp(sCurrentContext);
+
sCurrentContext = null;
Bridge.setLog(null);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 96ab30f..e5efa4e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -44,7 +44,6 @@
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@@ -124,7 +123,7 @@
// providing an XmlPullParser
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, context, resValue.isFramework());
@@ -206,7 +205,7 @@
// let the framework inflate the Drawable from the XML file.
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, context, value.isFramework());
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 3252fb49..70d5446 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -44,7 +44,7 @@
InputStream input = this.getClass().getClassLoader().getResourceAsStream(
"com/android/layoutlib/testdata/layout1.xml");
- parser.setInput(input, null /*encoding*/);
+ parser.setInput(input, "UTF-8"); //$NON-NLS-1$
assertEquals(XmlPullParser.START_DOCUMENT, parser.next());
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index eff6bbc..5c60318 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -99,8 +99,10 @@
"android.content.res.Resources$Theme#resolveAttribute",
"android.graphics.BitmapFactory#finishDecode",
"android.os.Handler#sendMessageAtTime",
+ "android.os.HandlerThread#run",
"android.os.Build#getString",
"android.view.LayoutInflater#rInflate",
+ "android.view.LayoutInflater#parseInclude",
"android.view.View#isInEditMode",
"com.android.internal.util.XmlUtils#convertValueToInt",
// TODO: comment out once DelegateClass is working