diff options
140 files changed, 5546 insertions, 760 deletions
@@ -127,11 +127,13 @@ LOCAL_SRC_FILES += \ 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 \ @@ -22250,10 +22250,8 @@ package android.view.accessibility { 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 8aabbd8..1f27774 100644 --- a/api/current.txt +++ b/api/current.txt @@ -90,6 +90,7 @@ package android { 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"; @@ -16438,6 +16439,7 @@ package android.renderscript { 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 +20437,12 @@ package android.view { 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,6 +20451,7 @@ package android.view { 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 @@ -20460,6 +20461,7 @@ package android.view { 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 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 +21479,7 @@ package android.view { 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 +21508,7 @@ package android.view { 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 +21677,7 @@ package android.view { 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 +22571,21 @@ package android.view.accessibility { 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 +22610,75 @@ package android.view.accessibility { } 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 +22691,7 @@ package android.view.accessibility { 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 @@ public class Am { 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 @@ public class Am { } } + private void runForceStop() throws Exception { + mAm.forceStopPackage(nextArgRequired()); + } + private void sendBroadcast() throws Exception { Intent intent = makeIntent(); IntentReceiver receiver = new IntentReceiver(); @@ -851,7 +857,7 @@ public class Am { wm.clearForcedDisplaySize(); } } catch (RemoteException e) { - } + } } private class IntentReceiver extends IIntentReceiver.Stub { @@ -1013,6 +1019,8 @@ public class Am { "\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.Message; 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 @@ import android.view.accessibility.AccessibilityEvent; * 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 @@ import android.view.accessibility.AccessibilityEvent; * 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 @@ import android.view.accessibility.AccessibilityEvent; * {@link AccessibilityServiceInfo#packageNames} * </strong> * </p> + * <p> + * For more details refer to {@link AccessibilityServiceInfo}. + * </p> * </li> * </ul> * <p> @@ -151,16 +162,49 @@ public abstract class AccessibilityService extends Service { * <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 @@ public abstract class AccessibilityService extends Service { /** * 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 @@ public abstract class BackupAgent extends ContextWrapper { 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 class FullBackup { 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 @@ public class FullBackup { 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 @@ public class FullBackup { } // 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.OsConstants; 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 @@ public class FullBackupAgent extends BackupAgent { @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 @@ public class FullBackupAgent extends BackupAgent { 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 @@ public class FullBackupAgent extends BackupAgent { String filePath = file.getAbsolutePath(); // prune this subtree? - if (excludes.contains(filePath)) { + if (excludes != null && excludes.contains(filePath)) { continue; } @@ -149,7 +151,7 @@ public class FullBackupAgent extends BackupAgent { } // 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 @@ public class FullBackupAgent extends BackupAgent { 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 @@ public final class BluetoothAdapter { /** * 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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/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 @@ public class PackageParser { } } + // 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 @@ public class Camera { * @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 @@ public class Camera { }; /** - * Area class for focus. - * - * @see #setFocusAreas(List) - * @see #getFocusAreas() + * Area class for focus and metering. + * + * @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 @@ public class Camera { 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 @@ public class Camera { * 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 @@ public class Camera { * 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 @@ public class Camera { 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/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 java.security.cert.X509Certificate; 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 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { 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 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } } - 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 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } 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/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 @@ public class ErrorCodes { 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 class ErrorCodes { 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 @@ import java.io.IOException; // 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/FileUtils.java b/core/java/android/os/FileUtils.java index eb0cf37..215e836 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -19,20 +19,21 @@ package android.os; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.util.regex.Pattern; +import java.util.zip.CRC32; +import java.util.zip.CheckedInputStream; /** * Tools for managing files. Not for public consumption. * @hide */ -public class FileUtils -{ +public class FileUtils { public static final int S_IRWXU = 00700; public static final int S_IRUSR = 00400; public static final int S_IWUSR = 00200; @@ -95,7 +96,7 @@ public class FileUtils /** returns the FAT file system volume ID for the volume mounted * at the given mount point, or -1 for failure - * @param mount point for FAT volume + * @param mountPoint point for FAT volume * @return volume ID or -1 */ public static native int getFatVolumeId(String mountPoint); @@ -243,4 +244,32 @@ public class FileUtils out.close(); } } + + /** + * Computes the checksum of a file using the CRC32 checksum routine. + * The value of the checksum is returned. + * + * @param file the file to checksum, must not be null + * @return the checksum value or an exception is thrown. + */ + public static long checksumCrc32(File file) throws FileNotFoundException, IOException { + CRC32 checkSummer = new CRC32(); + CheckedInputStream cis = null; + + try { + cis = new CheckedInputStream( new FileInputStream(file), checkSummer); + byte[] buf = new byte[128]; + while(cis.read(buf) >= 0) { + // Just read for checksum to get calculated. + } + return checkSummer.getValue(); + } finally { + if (cis != null) { + try { + cis.close(); + } catch (IOException e) { + } + } + } + } } 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.Cursor; 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 @@ import android.util.Log; * @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 @@ public final class Calendar { /** * 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 @@ public final class Calendar { } /** - * 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 final class Calendar { */ 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 final class Calendar { 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 @@ public final class Calendar { /** * 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 @@ public final class Calendar { /** * 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 @@ public final class Calendar { 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 @@ public final class Calendar { 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 @@ public final class Calendar { } /** - * 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 @@ public final class Calendar { /** * 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 @@ public final class Calendar { } /** - * 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 final class Calendar { 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 @@ public final class Calendar { /** * 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 @@ public final class Calendar { MAX_REMINDERS, CAN_MODIFY_TIME_ZONE, CAN_ORGANIZER_RESPOND, + CAN_PARTIALLY_UPDATE, CALENDAR_LOCATION, CALENDAR_TIMEZONE, ACCESS_LEVEL, @@ -457,7 +533,8 @@ public final class Calendar { CAL_SYNC2, CAL_SYNC3, CAL_SYNC4, - CAL_SYNC5, CAL_SYNC6, + CAL_SYNC5, + CAL_SYNC6, SYNC_STATE, }; } @@ -465,29 +542,29 @@ public final class Calendar { /** * 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 final class Calendar { 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 final class Calendar { 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 final class Calendar { public static final int ATTENDEE_STATUS_TENTATIVE = 4; } + /** + * Fields and helpers for interacting with Attendees. + */ public static final class Attendees implements BaseColumns, AttendeesColumns, EventsColumns { + + /** + * 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 + "=?"; - // TODO: fill out this class when we actually start utilizing attendees - // in the calendar application. + /** + * 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 @@ public final class Calendar { * 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 final class Calendar { */ 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 final class Calendar { 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 @@ public final class Calendar { /** * 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 @@ public final class Calendar { /** * 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 final class Calendar { 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 @@ public final class Calendar { 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 @@ public final class Calendar { } /** - * 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 final class Calendar { */ 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 @@ public final class Calendar { } /** - * 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 @@ public final class Calendar { 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); + return cr.query(builder.build(), projection, selection, selectionArgs, + orderBy == null ? DEFAULT_SORT_ORDER : 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 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 where, String orderBy) { + 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(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, + return cr.query(builder.build(), projection, selection, selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); } /** - * The content:// style URL for this table + * 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 @@ public final class Calendar { /** * 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 @@ public final class Calendar { * 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 final class Calendar { 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"); + private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + + "/instances/groupbyday"); + /** + * The projection used by the EventDays query. + */ public static final String[] PROJECTION = { STARTDAY, ENDDAY }; - public static final String SELECTION = "selected=1"; + private static final String SELECTION = "selected=1"; /** - * Retrieves the days with events for the Julian days starting at "startDay" - * for "numDays". + * 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 final class Calendar { } } - 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 @@ public final class Calendar { /** * 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 final class Calendar { 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 final class Calendar { 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 @@ public final class Calendar { 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 @@ public final class Calendar { 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 @@ public final class Calendar { /** * 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 @@ public final class Calendar { } } + /** + * 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 final class Calendar { } } - 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 @@ public final class Calendar { */ private SyncState() {} - public static final String CONTENT_DIRECTORY = + private static final String CONTENT_DIRECTORY = SyncStateContract.Constants.CONTENT_DIRECTORY; /** @@ -1732,39 +2065,43 @@ public final class Calendar { /** * 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/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 @@ class FinitePool<T extends Poolable<T>> implements Pool<T> { if (element != null) { element.setNextPoolable(null); + element.setPooled(false); mManager.onAcquired(element); } @@ -76,9 +77,13 @@ class FinitePool<T extends Poolable<T>> implements Pool<T> { } 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 @@ package android.util; 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..ba06795 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -109,16 +109,16 @@ public class Gravity */ 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_HORIZONTAL_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_HORIZONTAL_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 +342,8 @@ public class Gravity /** * <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 +351,11 @@ public class Gravity */ 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 layout is script specific and gravity is horizontal relative (START or END) if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) { - if ((result & Gravity.BEFORE) == Gravity.BEFORE) { - // Remove the BEFORE bit - result &= ~BEFORE; + if ((result & Gravity.START) == Gravity.START) { + // Remove the START bit + result &= ~START; if (isRtl) { // Set the RIGHT bit result |= RIGHT; @@ -363,9 +363,9 @@ public class Gravity // 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; 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 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { 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 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { 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..51eb13b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -61,6 +61,7 @@ import android.view.ContextMenu.ContextMenuInfo; 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; @@ -1492,6 +1493,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit 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 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit int mID = NO_ID; /** + * The stable ID of this view for accessibility porposes. + */ + int mAccessibilityViewId = NO_ID; + + /** * The view's tag. * {@hide} * @@ -3649,6 +3660,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * @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 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** + * 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 @@ -4571,6 +4689,16 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** + * 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 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit // 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 @@ -12719,6 +12847,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit ); private InvalidateInfo mNext; + private boolean mIsPooled; View target; @@ -12742,6 +12871,14 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit void release() { sPool.release(this); } + + public boolean isPooled() { + return mIsPooled; + } + + public void setPooled(boolean isPooled) { + mIsPooled = isPooled; + } } final IWindowSession mSession; @@ -12952,6 +13089,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit 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.AndroidRuntimeException; 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.IOException; 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 @@ public final class ViewAncestor extends Handler implements ViewParent, */ AudioManager mAudioManager; + final AccessibilityManager mAccessibilityManager; + + AccessibilityInteractionController mAccessibilityInteractionContrtoller; + + AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager; + private final int mDensity; /** @@ -285,7 +302,7 @@ public final class ViewAncestor extends Handler implements ViewParent, // 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 @@ public final class ViewAncestor extends Handler implements ViewParent, 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 @@ public final class ViewAncestor extends Handler implements ViewParent, 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 @@ public final class ViewAncestor extends Handler implements ViewParent, mView.dispatchDetachedFromWindow(); } + mAccessibilityInteractionConnectionManager.ensureNoConnection(); + mAccessibilityManager.removeAccessibilityStateChangeListener( + mAccessibilityInteractionConnectionManager); + mView = null; mAttachInfo.mRootView = null; mAttachInfo.mSurface = null; @@ -2020,7 +2050,6 @@ public final class ViewAncestor extends Handler implements ViewParent, InputQueue.unregisterInputChannel(mInputChannel); } } - try { sWindowSession.remove(mWindow); } catch (RemoteException e) { @@ -2098,6 +2127,10 @@ public final class ViewAncestor extends Handler implements ViewParent, 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 @@ public final class ViewAncestor extends Handler implements ViewParent, 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 @@ public final class ViewAncestor extends Handler implements ViewParent, 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 @@ public final class ViewAncestor extends Handler implements ViewParent, * 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 @@ public final class ViewAncestor extends Handler implements ViewParent, if (mView == null) { return false; } - AccessibilityManager.getInstance(child.mContext).sendAccessibilityEvent(event); + mAccessibilityManager.sendAccessibilityEvent(event); return true; } @@ -3608,18 +3668,18 @@ public final class ViewAncestor extends Handler implements ViewParent, 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 @@ public final class ViewAncestor extends Handler implements ViewParent, 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 final class ViewAncestor extends Handler implements ViewParent, } 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 final class ViewAncestor extends Handler implements ViewParent, } 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 final class ViewAncestor extends Handler implements ViewParent, } } } - + public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, boolean sync) { if (sync) { @@ -3733,17 +3794,16 @@ public final class ViewAncestor extends Handler implements ViewParent, /* 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 @@ public final class ViewAncestor extends Handler implements ViewParent, } } + /** + * 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/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.AttributeSet; 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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + @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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager 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 @@ public interface WindowManager extends ViewManager { 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 7b80797..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 @@ import java.util.ArrayList; * @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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par 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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * 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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** + * 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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * 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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * 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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * 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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * 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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * {@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 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par 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.RemoteException; 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 @@ public final class AccessibilityManager { 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 final class AccessibilityManager { 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 @@ public final class AccessibilityManager { 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 @@ public final class AccessibilityManager { } 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 @@ public class AccessibilityRecord { 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 @@ public class AccessibilityRecord { } /** + * 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 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { * 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,27 +143,33 @@ public class AccessibilityRecord { * 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. + * Gets if the source is taking the entire screen. * - * @param isFullScreen True if the source is full screen, false otherwise. + * @return True if the source is full screen, false otherwise. */ - public void setFullScreen(boolean isFullScreen) { - setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen); + public boolean isFullScreen() { + return getBooleanProperty(PROPERTY_FULL_SCREEN); } /** - * Gets if the source is taking the entire screen. + * Sets if the source is taking the entire screen. * - * @return True if the source is full screen, false otherwise. + * @param isFullScreen True if the source is full screen, false otherwise. + * + * @throws IllegalStateException If called from an AccessibilityService. */ - public boolean isFullScreen() { - return getBooleanProperty(PROPERTY_FULL_SCREEN); + public void setFullScreen(boolean isFullScreen) { + enforceNotSealed(); + setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen); } /** @@ -152,8 +185,11 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { * 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 @@ public class AccessibilityRecord { /** * 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 @@ public class AccessibilityRecord { /** * 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 @@ interface IAccessibilityManager { 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..a68ca60 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -6503,6 +6503,8 @@ public class WebView extends AbsoluteLayout // 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 @@ public class WebView extends AbsoluteLayout // 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout } 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 @@ public class FrameLayout extends ViewGroup { } /** - * 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 class FrameLayout extends ViewGroup { 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..3876735 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -116,10 +116,10 @@ public class LinearLayout extends ViewGroup { 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, @@ -135,7 +135,7 @@ public class LinearLayout extends ViewGroup { @ViewDebug.FlagToString(mask = Gravity.RELATIVE_HORIZONTAL_DIRECTION, equals = Gravity.RELATIVE_HORIZONTAL_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 class LinearLayout extends ViewGroup { 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 @@ public class LinearLayout extends ViewGroup { @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.ViewDebug; 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 class RelativeLayout extends ViewGroup { 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 @@ public class RelativeLayout extends ViewGroup { ); private Node mNext; + private boolean mIsPooled; public void setNextPoolable(Node element) { mNext = element; @@ -1449,6 +1451,14 @@ public class RelativeLayout extends ViewGroup { 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.ViewTreeObserver; 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 class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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/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.os.RemoteException; 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..942aa8a 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -755,32 +755,46 @@ public: 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::drawTextRun(SkPaint* paint, const jchar* chars, 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) { +#else + value = new TextLayoutCacheValue(); + value->computeValues(paint, chars, start, count, contextCount, dirFlags); +#endif + if (value != NULL) { if (resultAdvances != NULL) { - memcpy(resultAdvances, layout->getAdvances(), layout->getAdvancesCount() * sizeof(jfloat)); + memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat)); } - resultTotalAdvance = layout->getTotalAdvance(); + resultTotalAdvance = value->getTotalAdvance(); } -#else - // Compute advances and return them - TextLayoutCacheValue::computeValuesWithHarfbuzz(paint, chars, start, count, contextCount, - dirFlags, resultAdvances, &resultTotalAdvance, NULL, NULL ); -#endif } 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 struct { 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 e5c2848..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 android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject clazz, 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 @@ static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, 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 Binary files differnew file mode 100644 index 0000000..fbd187c --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_arrow.png 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 Binary files differnew file mode 100644 index 0000000..d7aca36 --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_spot_anchor.png 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 Binary files differnew file mode 100644 index 0000000..5041aa3 --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_spot_hover.png 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 Binary files differnew file mode 100644 index 0000000..64a42a1 --- /dev/null +++ b/core/res/res/drawable-hdpi/pointer_spot_touch.png 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/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 bf1d432..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 7fb4f7318..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/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/strings.xml b/core/res/res/values/strings.xml index 8a4b74b..29c23a6 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/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/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 @@ public class GravityTest extends AndroidTestCase { 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/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 class Allocation extends BaseObj { 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/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java index 6487623..fac7144 100644 --- a/graphics/java/android/renderscript/FieldPacker.java +++ b/graphics/java/android/renderscript/FieldPacker.java @@ -51,7 +51,7 @@ public class FieldPacker { public void skip(int i) { int res = mPos + i; - if ((res < 0) || (res >= mLen)) { + if ((res < 0) || (res > mLen)) { throw new RSIllegalArgumentException("out of range argument: " + i); } mPos = res; 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 @@ public: // 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 @@ public: // 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 @@ public: 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/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_YUV420SP[] = "yuv420sp"; 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/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index f933199..b433fd3 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -503,10 +503,10 @@ int write_tarfile(const String8& packageName, const String8& domain, needExtended = true; } - // Non-7bit-clean path also means needing pax extended format + // Non-7bit-clean path or embedded spaces also mean needing pax extended format if (!needExtended) { for (size_t i = 0; i < filepath.length(); i++) { - if ((filepath[i] & 0x80) != 0) { + if ((filepath[i] & 0x80) != 0 || filepath[i] == ' ') { needExtended = true; break; } 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 class MediaFile { 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 @@ public class MediaFile { 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 @@ namespace android { 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 @@ int JetPlayer::render() { // 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 @@ status_t MediaPlayerService::Client::dump(int fd, const Vector<String16>& args) 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 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) } 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 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) 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 @@ int MidiFile::render() { // 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 @@ void MPEG4Writer::writeGeoDataBox() { * 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/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk new file mode 100644 index 0000000..1d4f4da --- /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/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 @@ private: } 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 @@ private: // 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) + { + 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) { - handle.data = getHeap()->base(); - handle.size = size; + 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 @@ private: 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/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 7c6d3c1..b568af1 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -134,6 +134,7 @@ class BackupManagerService extends IBackupManager.Stub { // 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { } } - // 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 @@ class BackupManagerService extends IBackupManager.Stub { } } + 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 @@ class BackupManagerService extends IBackupManager.Stub { } 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); - - // 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); + 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. + 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 @@ class BackupManagerService extends IBackupManager.Stub { // 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 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 @@ public class SystemBackupAgent extends BackupAgentHelper { 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/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.IntentFilter; 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.IBinder; 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.Iterator; 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private boolean mHasInputFilter; + private final WindowManagerService mWindowManagerService; + + private final SecurityPolicy mSecurityPolicy; + /** * Handler for delayed event dispatch. */ @@ -145,6 +165,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } - + super.onReceive(context, intent); } }; @@ -297,6 +319,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public boolean sendAccessibilityEvent(AccessibilityEvent event) { synchronized (mLock) { + mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event); notifyAccessibilityServicesDelayedLocked(event, false); notifyAccessibilityServicesDelayedLocked(event, true); } @@ -352,7 +375,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * spoken feedback. */ private void updateInputFilterLocked() { - WindowManagerService wm = (WindowManagerService)ServiceManager.getService( - Context.WINDOW_SERVICE); if (mIsEnabled) { final boolean hasSpokenFeedbackServices = !getEnabledAccessibilityServiceList( AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty(); @@ -695,20 +778,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (mInputFilter == null) { mInputFilter = new AccessibilityInputFilter(mContext); } - wm.setInputFilter(mInputFilter); + mWindowManagerService.setInputFilter(mInputFilter); mHasInputFilter = true; - } else { - if (mHasInputFilter) { - wm.setInputFilter(null); - mHasInputFilter = false; - } - } - } else { - if (mHasInputFilter) { - wm.setInputFilter(null); - mHasInputFilter = false; + return; } } + if (mHasInputFilter) { + mWindowManagerService.setInputFilter(null); + mHasInputFilter = false; + } } /** @@ -743,24 +821,38 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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)); - } - - public void setAccessibilityServiceInfo(AccessibilityServiceInfo info) { - mAccessibilityServiceInfo = info; - ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo; - mComponentName = new ComponentName(serviceInfo.packageName, serviceInfo.name); + 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 setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) { mEventTypes = info.eventTypes; mFeedbackType = info.feedbackType; String[] packageNames = info.packageNames; @@ -781,7 +873,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * @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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { tryRemoveServiceLocked(this); } - mContext.unbindService(this); + if (!mIsFake) { + mContext.unbindService(this); + } mService = null; return true; } @@ -832,6 +926,154 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index e8155b4..7ea0591 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -1212,8 +1212,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { 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/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 @@ public class SmsCbMessage { 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 @@ public class SmsCbMessage { 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 @@ public class SmsCbMessage { 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 @@ public class SmsCbMessage { 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 @@ public class SmsCbMessage { 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/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 class SmsCbHeader { 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) { + pageIndex = 1; + nrOfPages = 1; + } - if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) { + 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 @@ public class GsmSmsCbTest extends AndroidTestCase { 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 @@ public class GsmSmsCbTest extends AndroidTestCase { 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 @@ public class GsmSmsCbTest extends AndroidTestCase { 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 @@ public class GsmSmsCbTest extends AndroidTestCase { 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 @@ public class GsmSmsCbTest extends AndroidTestCase { "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 @@ public class GsmSmsCbTest extends AndroidTestCase { 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 @@ public class GsmSmsCbTest extends AndroidTestCase { 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 @@ public class GsmSmsCbTest extends AndroidTestCase { 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 @@ public class GsmSmsCbTest extends AndroidTestCase { 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/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 Binary files differnew file mode 100755 index 0000000..f9d6172 --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/res/drawable/globe.png 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 @@ public class RsBenchRS { 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 @@ public class RsBenchRS { 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 @@ public class RsBenchRS { 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 @@ public class RsBenchRS { 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 @@ public class RsBenchRS { 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 gTexOpaque; 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 displayFontSamples(int fillNum) { } static void bindProgramVertexOrtho() { - // Default vertex sahder + // Default vertex shader rsgBindProgramVertex(gProgVertex); // Setup the projection matrix rs_matrix4x4 proj; @@ -198,6 +206,33 @@ static void displayMeshSamples(int meshNum) { } } +// 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 @@ static const char *testNames[] = { "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 @@ static void runTest(int index) { case 24: displayPixelLightSamples(8, true); break; + case 25: + displayMeshWithMultiTexture(0); + break; + case 26: + displayMeshWithMultiTexture(1); + break; } } diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java index 0129114..b212533 100644 --- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java +++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java @@ -145,13 +145,10 @@ public class StatusBarTest extends TestActivity }, new Test("Priority notification") { public void run() { - Notification not = new Notification(StatusBarTest.this, + Notification not = new Notification( R.drawable.stat_sys_phone, "Incoming call from: Imperious Leader", - System.currentTimeMillis()-(1000*60*60*24), - "Imperious Leader", - "(888) 555-5038", - null + System.currentTimeMillis()-(1000*60*60*24) ); not.flags |= Notification.FLAG_HIGH_PRIORITY; Intent fullScreenIntent = new Intent(StatusBarTest.this, TestAlertActivity.class); @@ -248,12 +245,10 @@ public class StatusBarTest extends TestActivity mHandler.postDelayed(new Runnable() { public void run() { mNotificationManager.notify(1, - new Notification(StatusBarTest.this, + new Notification( R.drawable.ic_statusbar_missedcall, "tick tick tick", - System.currentTimeMillis()-(1000*60*60*24), - "(453) 123-2328", - "", null + System.currentTimeMillis()-(1000*60*60*24) )); } }, 3000); |
