From 80aea03d46272a47dd7e9ba5f98a70f2d67cfd84 Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Wed, 18 Jul 2012 10:05:03 -0400 Subject: [PATCH 1/8] Fix systemui crash on Xoom startup Make new bugdroid drawable available to non-hdpi device builds. (moved to no-dpi for now) It was getting optimized out during the build process. Bug: 6828023 Change-Id: I31ac6ce2f50edc7f65d37b4b0f7513dd33a8b22d --- .../{drawable-hdpi => drawable-nodpi}/bugdroid.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/SystemUI/res/{drawable-hdpi => drawable-nodpi}/bugdroid.png (100%) diff --git a/packages/SystemUI/res/drawable-hdpi/bugdroid.png b/packages/SystemUI/res/drawable-nodpi/bugdroid.png similarity index 100% rename from packages/SystemUI/res/drawable-hdpi/bugdroid.png rename to packages/SystemUI/res/drawable-nodpi/bugdroid.png From a70074ee4b32aa99ff55ba01c321194b9f5f83e6 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 9 Jan 2013 09:46:54 -0800 Subject: [PATCH 2/8] Fix media scan NPE b/7966288 Change-Id: I066bc9f00bccf070332d87d6f1bf3d6054b31136 --- media/java/android/media/MediaScanner.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index fbbcb0a323e58..7768a61968b56 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -958,7 +958,9 @@ public class MediaScanner // If the rowId of the inserted file is needed, it gets inserted immediately, // bypassing the bulk inserter. if (inserter == null || needToSetSettings) { - inserter.flushAll(); + if (inserter != null) { + inserter.flushAll(); + } result = mMediaProvider.insert(tableUri, values); } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) { inserter.insertwithPriority(tableUri, values); From b225548244cd79ad251194a6b1975350ba8bdfe5 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 9 Jan 2013 15:54:56 -0800 Subject: [PATCH 3/8] Revert "App home directories are now 0700 for targetSdkVersion > 17" This reverts commit 92091fa9636403728fe94cc83400495a8612c2d3. Bug: 7966399 Bug: 7208882 --- api/current.txt | 1 - core/java/android/content/Context.java | 96 ++++++------------- core/java/android/os/Build.java | 11 --- .../java/com/android/server/pm/Installer.java | 18 +--- .../server/pm/PackageManagerService.java | 19 ++-- .../java/com/android/server/pm/Settings.java | 6 +- 6 files changed, 45 insertions(+), 106 deletions(-) diff --git a/api/current.txt b/api/current.txt index e4d5fd1f188c1..cf24f1347e51c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15737,7 +15737,6 @@ package android.os { field public static final int ICE_CREAM_SANDWICH_MR1 = 15; // 0xf field public static final int JELLY_BEAN = 16; // 0x10 field public static final int JELLY_BEAN_MR1 = 17; // 0x11 - field public static final int K = 10000; // 0x2710 } public final class Bundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 257f84ef8e942..7aa2507975dc2 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -64,44 +64,30 @@ public abstract class Context { */ public static final int MODE_PRIVATE = 0x0000; /** + * @deprecated Creating world-readable files is very dangerous, and likely + * to cause security holes in applications. It is strongly discouraged; + * instead, applications should use more formal mechanism for interactions + * such as {@link ContentProvider}, {@link BroadcastReceiver}, and + * {@link android.app.Service}. There are no guarantees that this + * access mode will remain on a file, such as when it goes through a + * backup and restore. * File creation mode: allow all other applications to have read access * to the created file. - * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, world-readable files created within the - * application's home directory will not be accessible to other applications. - * - * @deprecated Creating world-readable files is very dangerous, and likely - * to cause security holes in applications. It is strongly discouraged; - * instead, applications should use more formal mechanism for interactions - * such as {@link ContentProvider}, {@link BroadcastReceiver}, and - * {@link android.app.Service}. There are no guarantees that this - * access mode will remain on a file, such as when it goes through a - * backup and restore. * @see #MODE_PRIVATE * @see #MODE_WORLD_WRITEABLE */ @Deprecated public static final int MODE_WORLD_READABLE = 0x0001; /** + * @deprecated Creating world-writable files is very dangerous, and likely + * to cause security holes in applications. It is strongly discouraged; + * instead, applications should use more formal mechanism for interactions + * such as {@link ContentProvider}, {@link BroadcastReceiver}, and + * {@link android.app.Service}. There are no guarantees that this + * access mode will remain on a file, such as when it goes through a + * backup and restore. * File creation mode: allow all other applications to have write access * to the created file. - * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, world-writable files created within the - * application's home directory will not be accessible to other applications. - * - * @deprecated Creating world-writable files is very dangerous, and likely - * to cause security holes in applications. It is strongly discouraged; - * instead, applications should use more formal mechanism for interactions - * such as {@link ContentProvider}, {@link BroadcastReceiver}, and - * {@link android.app.Service}. There are no guarantees that this - * access mode will remain on a file, such as when it goes through a - * backup and restore. * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE */ @@ -515,22 +501,18 @@ public abstract class Context { throws FileNotFoundException; /** - * Open a file associated with this Context's application package + * Open a private file associated with this Context's application package * for writing. Creates the file if it doesn't already exist. * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, files created with {@code mode} - * {@link #MODE_WORLD_READABLE} or {@link #MODE_WORLD_WRITEABLE} will not be - * accessible to other applications. - * - * @param name The name of the file to open; can not contain path separators. + * @param name The name of the file to open; can not contain path + * separators. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the - * default operation, {@link #MODE_APPEND} to append to an existing file, - * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control - * permissions. + * default operation, {@link #MODE_APPEND} to append to an existing file, + * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control + * permissions. + * * @return FileOutputStream Resulting output stream. + * * @see #MODE_APPEND * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE @@ -756,19 +738,14 @@ public abstract class Context { * application; you can only set the mode of the entire directory, not * of individual files. * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, world-readable / world-writable directories - * created using this method will not be accessible to other applications. - * * @param name Name of the directory to retrieve. This is a directory - * that is created as part of your application data. + * that is created as part of your application data. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the - * default operation, {@link #MODE_WORLD_READABLE} and - * {@link #MODE_WORLD_WRITEABLE} to control permissions. + * default operation, {@link #MODE_WORLD_READABLE} and + * {@link #MODE_WORLD_WRITEABLE} to control permissions. + * * @return Returns a File object for the requested directory. The directory - * will have been created if it does not already exist. + * will have been created if it does not already exist. * * @see #openFileOutput(String, int) */ @@ -778,13 +755,6 @@ public abstract class Context { * Open a new private SQLiteDatabase associated with this Context's * application package. Create the database file if it doesn't exist. * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, {@code SQLiteDatabase}s created with {@code mode} - * {@link #MODE_WORLD_READABLE} or {@link #MODE_WORLD_WRITEABLE} will not be - * accessible to other applications. - * * @param name The name (unique in the application package) of the database. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the * default operation, {@link #MODE_WORLD_READABLE} @@ -792,8 +762,10 @@ public abstract class Context { * Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default. * @param factory An optional factory class that is called to instantiate a * cursor when query is called. + * * @return The contents of a newly created database with the given name. * @throws android.database.sqlite.SQLiteException if the database file could not be opened. + * * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE @@ -810,13 +782,6 @@ public abstract class Context { *

Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be * used to handle corruption when sqlite reports database corruption.

* - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, {@code SQLiteDatabase}s created with {@code mode} - * {@link #MODE_WORLD_READABLE} or {@link #MODE_WORLD_WRITEABLE} will not be - * accessible to other applications. - * * @param name The name (unique in the application package) of the database. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the * default operation, {@link #MODE_WORLD_READABLE} @@ -825,9 +790,10 @@ public abstract class Context { * @param factory An optional factory class that is called to instantiate a * cursor when query is called. * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database - * corruption. if null, {@link android.database.DefaultDatabaseErrorHandler} is assumed. + * corruption. if null, {@link android.database.DefaultDatabaseErrorHandler} is assumed. * @return The contents of a newly created database with the given name. * @throws android.database.sqlite.SQLiteException if the database file could not be opened. + * * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index d69d2a658e180..a7f39d5ef1ac5 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -431,17 +431,6 @@ public class Build { * */ public static final int JELLY_BEAN_MR1 = 17; - - /** - * Android X.X: "K". Just "K" - * - *

Applications targeting this or a later release will get these - * new changes in behavior:

- *
    - *
  • Application home directory permissions are now {@code 0700}.
  • - *
- */ - public static final int K = CUR_DEVELOPMENT; } /** The type of build, like "user" or "eng". */ diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java index e96820f266977..02a2c1b0c7110 100644 --- a/services/java/com/android/server/pm/Installer.java +++ b/services/java/com/android/server/pm/Installer.java @@ -188,12 +188,7 @@ public final class Installer { } } - /** - * @param restrictHomeDir if {@code true}, installd will create the application's - * home directory with {@code 0700} permissions. If false, {@code 0751} will - * be used instead. - */ - public int install(String name, int uid, int gid, boolean restrictHomeDir) { + public int install(String name, int uid, int gid) { StringBuilder builder = new StringBuilder("install"); builder.append(' '); builder.append(name); @@ -201,8 +196,6 @@ public final class Installer { builder.append(uid); builder.append(' '); builder.append(gid); - builder.append(' '); - builder.append(restrictHomeDir); return execute(builder.toString()); } @@ -270,12 +263,7 @@ public final class Installer { return execute(builder.toString()); } - /** - * @param restrictHomeDir if {@code true}, installd will create the application's - * home directory with {@code 0700} permissions. If false, {@code 0751} will - * be used instead. - */ - public int createUserData(String name, int uid, int userId, boolean restrictHomeDir) { + public int createUserData(String name, int uid, int userId) { StringBuilder builder = new StringBuilder("mkuserdata"); builder.append(' '); builder.append(name); @@ -283,8 +271,6 @@ public final class Installer { builder.append(uid); builder.append(' '); builder.append(userId); - builder.append(' '); - builder.append(restrictHomeDir); return execute(builder.toString()); } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index aa2b461216f7e..2238f17621f1c 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.ArrayUtils.removeInt; +import static libcore.io.OsConstants.S_ISLNK; import static libcore.io.OsConstants.S_IRWXU; import static libcore.io.OsConstants.S_IRGRP; import static libcore.io.OsConstants.S_IXGRP; @@ -110,6 +111,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.Environment.UserEnvironment; +import android.provider.Settings.Secure; import android.security.SystemKeyStore; import android.util.DisplayMetrics; import android.util.EventLog; @@ -146,11 +148,13 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import libcore.io.ErrnoException; import libcore.io.IoUtils; import libcore.io.Libcore; +import libcore.io.OsConstants; import libcore.io.StructStat; /** @@ -3578,17 +3582,16 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private int createDataDirsLI(String packageName, int uid, int targetSdkVersion) { + private int createDataDirsLI(String packageName, int uid) { int[] users = sUserManager.getUserIds(); - boolean restrictHomeDir = (targetSdkVersion >= Build.VERSION_CODES.K); - int res = mInstaller.install(packageName, uid, uid, restrictHomeDir); + int res = mInstaller.install(packageName, uid, uid); if (res < 0) { return res; } for (int user : users) { if (user != 0) { res = mInstaller.createUserData(packageName, - UserHandle.getUid(user, uid), user, restrictHomeDir); + UserHandle.getUid(user, uid), user); if (res < 0) { return res; } @@ -3982,8 +3985,7 @@ public class PackageManagerService extends IPackageManager.Stub { recovered = true; // And now re-install the app. - ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.targetSdkVersion); + ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid); if (ret == -1) { // Ack should not happen! msg = prefix + pkg.packageName @@ -4028,9 +4030,8 @@ public class PackageManagerService extends IPackageManager.Stub { if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) Log.v(TAG, "Want this data dir: " + dataPath); } - // invoke installer to do the actual installation - int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.targetSdkVersion); + //invoke installer to do the actual installation + int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid); if (ret < 0) { // Error from installer mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index f791a6e6e148d..06f11bc9a4dab 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -25,6 +25,7 @@ import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; +import com.android.server.IntentResolver; import com.android.server.pm.PackageManagerService.DumpState; import org.xmlpull.v1.XmlPullParser; @@ -45,7 +46,6 @@ import android.content.pm.UserInfo; import android.content.pm.PackageUserState; import android.content.pm.VerifierDeviceIdentity; import android.os.Binder; -import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.Process; @@ -2335,11 +2335,9 @@ final class Settings { for (PackageSetting ps : mPackages.values()) { // Only system apps are initially installed. ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle); - boolean restrictHomeDir = (ps.pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.K); // Need to create a data directory for all apps under this user. installer.createUserData(ps.name, - UserHandle.getUid(userHandle, ps.appId), userHandle, - restrictHomeDir); + UserHandle.getUid(userHandle, ps.appId), userHandle); } readDefaultPreferredAppsLPw(userHandle); writePackageRestrictionsLPr(userHandle); From f623b930d3661540f74370d3548ac0a056c3320b Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 9 Jan 2013 16:03:14 -0800 Subject: [PATCH 4/8] Add K api version. Change-Id: I2b74e06f8bda8b23c5cc4280af4b18a5ad66a6c2 --- api/current.txt | 1 + core/java/android/os/Build.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/api/current.txt b/api/current.txt index cf24f1347e51c..e4d5fd1f188c1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15737,6 +15737,7 @@ package android.os { field public static final int ICE_CREAM_SANDWICH_MR1 = 15; // 0xf field public static final int JELLY_BEAN = 16; // 0x10 field public static final int JELLY_BEAN_MR1 = 17; // 0x11 + field public static final int K = 10000; // 0x2710 } public final class Bundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index a7f39d5ef1ac5..52c89e868cf04 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -431,6 +431,11 @@ public class Build { * */ public static final int JELLY_BEAN_MR1 = 17; + + /** + * Android X.X: "K". Just "K" + */ + public static final int K = CUR_DEVELOPMENT; } /** The type of build, like "user" or "eng". */ From 1efbb80d5e4ccc44419f823fb7bd3bd0e25b332d Mon Sep 17 00:00:00 2001 From: Laurent Tu Date: Thu, 24 Jan 2013 15:10:24 -0800 Subject: [PATCH 5/8] Fix GpsLocationProvider.enable/disable/isEnabled Fix the issue where GpsLocationProvider.isEnabled() returns true when it is really false (and the other way around), when the handler hasn't processed the enable/disable messages yet. This can be systematically reproduced when the caller code is using the same thread as the thread of the handler in GpsLocationProvider. For example, this was happening in LocationManagerService.switchUser(). It would start by disabling all the providers (with updateProviderListenersLocked()), then re-enable them in updateProvidersLocked() only when isEnalbed()==false, which was in the wrong state since the GpsLocationProvider.ENABLE message hadn't been processed yet. As a result, the GpsLocationProvider was disabled upon startup of the phone. This is a slight problem for the enable() contract, which specifies that getStatus() must be handled, getStatus() will be handled but might have slighty not-up-to-date info in this case. Bug: 8028017 Change-Id: Iff91a11cc150e9029a6db85b64a10a926e12b0ba --- .../server/location/GpsLocationProvider.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 12c14bfa626c4..da150d9e6ca64 100644 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -704,17 +704,17 @@ public class GpsLocationProvider implements LocationProviderInterface { */ @Override public void enable() { + synchronized (mLock) { + if (mEnabled) return; + mEnabled = true; + } + sendMessage(ENABLE, 1, null); } private void handleEnable() { if (DEBUG) Log.d(TAG, "handleEnable"); - synchronized (mLock) { - if (mEnabled) return; - mEnabled = true; - } - boolean enabled = native_init(); if (enabled) { @@ -740,17 +740,17 @@ public class GpsLocationProvider implements LocationProviderInterface { */ @Override public void disable() { + synchronized (mLock) { + if (!mEnabled) return; + mEnabled = false; + } + sendMessage(ENABLE, 0, null); } private void handleDisable() { if (DEBUG) Log.d(TAG, "handleDisable"); - synchronized (mLock) { - if (!mEnabled) return; - mEnabled = false; - } - stopNavigating(); mAlarmManager.cancel(mWakeupIntent); mAlarmManager.cancel(mTimeoutIntent); From 8c5eb4a3d94241665663bc2b2aaaa8a15e3bafbd Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 30 Jan 2013 10:58:40 -0800 Subject: [PATCH 6/8] Check text length when testing for newline. Also add tests to verify. Bug: 8102140 Change-Id: I7e5dbff53caeb50bfa0fb4ea5dce73e3c742986a --- .../internal/util/FastXmlSerializer.java | 2 +- .../internal/util/FastXmlSerializerTest.java | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java index 99eea151879fd..7a040801a9a50 100644 --- a/core/java/com/android/internal/util/FastXmlSerializer.java +++ b/core/java/com/android/internal/util/FastXmlSerializer.java @@ -390,7 +390,7 @@ public class FastXmlSerializer implements XmlSerializer { } escapeAndAppendString(text); if (mIndent) { - mLineStart = text.charAt(text.length()-1) == '\n'; + mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n'); } return this; } diff --git a/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java b/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java new file mode 100644 index 0000000000000..be7116dffb899 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import junit.framework.TestCase; + +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayOutputStream; + +/** + * Tests for {@link FastXmlSerializer} + */ +public class FastXmlSerializerTest extends TestCase { + public void testEmptyText() throws Exception { + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + final XmlSerializer out = new FastXmlSerializer(); + out.setOutput(stream, "utf-8"); + out.startDocument(null, true); + out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + out.startTag(null, "string"); + out.attribute(null, "name", "meow"); + out.text(""); + out.endTag(null, "string"); + + out.endDocument(); + + assertEquals("\n" + + "", stream.toString()); + } +} From e28ecab813c5f15b117854fe4a3fac1f77d9b5b7 Mon Sep 17 00:00:00 2001 From: Mike Cleron Date: Fri, 1 Feb 2013 18:36:41 +0000 Subject: [PATCH 7/8] Revert "Fixing backwards cmpatibility for enabling explore by touch" This reverts commit 0a9c7c144671f70f5f1af222050bd3ec6b11fe41 Change-Id: I8da530a6d0a57163522d5a63326e787a8d22ecbf --- .../AccessibilityServiceInfo.java | 10 +- core/java/android/provider/Settings.java | 1 + core/res/res/values/strings.xml | 23 +- .../providers/settings/DatabaseHelper.java | 18 +- .../AccessibilityManagerService.java | 251 +++--------------- 5 files changed, 57 insertions(+), 246 deletions(-) diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index d12a336b13b26..ef7186bb6a694 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -150,18 +150,10 @@ public class AccessibilityServiceInfo implements Parcelable { * flag does not guarantee that the device will not be in touch exploration * mode since there may be another enabled service that requested it. *

- * For accessibility services targeting API version higher than - * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set - * this flag have to request the + * Clients that want to set this flag have to request the * {@link android.Manifest.permission#CAN_REQUEST_TOUCH_EXPLORATION_MODE} * permission or the flag will be ignored. *

- *

- * Services targeting API version equal to or lower than - * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e. - * the first time they are run, if this flag is specified, a dialog is - * shown to the user to confirm enabling explore by touch. - *

*/ public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 3d850cfb022dd..be21fb433f961 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3351,6 +3351,7 @@ public final class Settings { * * @hide */ + @Deprecated public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES = "touch_exploration_granted_accessibility_services"; diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index aae509edcdfc6..8f8f32de7c28d 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -950,8 +950,8 @@ request enhanced web accessibility Allows the hoder to request - enabling of web accessibility enhancements. For example, installing scripts to make - app content more accessible. + enabling of web accessibility enhancements. For example, installing scripts from + Google to make app content more accessible. bind to a text service @@ -2714,25 +2714,6 @@ Voice search - - Enable Explore by Touch? - - - %1$s wants to enable Explore by Touch. - When Explore by Touch is turned on, you can hear or see descriptions of what\'s under - your finger or perform gestures to interact with the tablet. - - - %1$s wants to enable Explore by Touch. - When Explore by Touch is turned on, you can hear or see descriptions of what\'s under - your finger or perform gestures to interact with the phone. - 1 month ago diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index fc0ff559c5cc9..695c2aa708b85 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -71,7 +71,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 96; + private static final int DATABASE_VERSION = 97; private Context mContext; private int mUserHandle; @@ -1536,6 +1536,22 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 96; } + if (upgradeVersion == 96) { + // Remove Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES + if (mUserHandle == UserHandle.USER_OWNER) { + db.beginTransaction(); + try { + db.execSQL("DELETE FROM system WHERE name='" + + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES + + "'"); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + upgradeVersion = 97; + } + // *** Remember to update DATABASE_VERSION above! if (upgradeVersion != currentVersion) { diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index bb040bffd6f2f..f4592f78cf64d 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -23,15 +23,12 @@ import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; -import android.app.AlertDialog; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; @@ -70,7 +67,6 @@ import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MagnificationSpec; -import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityManager; @@ -161,8 +157,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private Service mQueryBridge; - private AlertDialog mEnableTouchExplorationDialog; - private AccessibilityInputFilter mInputFilter; private boolean mHasInputFilter; @@ -250,12 +244,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { persistComponentNamesToSettingLocked( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, state.mEnabledServices, userId); - // Update the touch exploration granted services setting. - state.mTouchExplorationGrantedServices.remove(comp); - persistComponentNamesToSettingLocked( - Settings.Secure. - TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, - state.mEnabledServices, userId); return; } } @@ -570,8 +558,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mIsDisplayMagnificationEnabled = false; userState.mEnabledServices.clear(); userState.mEnabledServices.add(service); - userState.mTouchExplorationGrantedServices.clear(); - userState.mTouchExplorationGrantedServices.add(service); // Update the internal state. performServiceManagementLocked(userState); scheduleUpdateInputFilter(userState); @@ -845,12 +831,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mEnabledServices); } - private void populateTouchExplorationGrantedAccessibilityServicesLocked(UserState userState) { - populateComponentNamesFromSettingLocked( - Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, - userState.mUserId, userState.mTouchExplorationGrantedServices); - } - /** * Performs {@link AccessibilityService}s delayed notification. The delay is configurable * and denotes the period after the last event before notifying the service. @@ -1143,54 +1123,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } - private void showEnableTouchExplorationDialog(final Service service) { - String label = service.mResolveInfo.loadLabel( - - mContext.getPackageManager()).toString(); - synchronized (mLock) { - final UserState state = getCurrentUserStateLocked(); - if (state.mIsTouchExplorationEnabled) { - return; - } - if (mEnableTouchExplorationDialog != null - && mEnableTouchExplorationDialog.isShowing()) { - return; - } - mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setPositiveButton(android.R.string.ok, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // The user allowed the service to toggle touch exploration. - state.mTouchExplorationGrantedServices.add(service.mComponentName); - persistComponentNamesToSettingLocked( - Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, - state.mTouchExplorationGrantedServices, state.mUserId); - // Enable touch exploration. - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, - service.mUserId); - } - }) - .setNegativeButton(android.R.string.cancel, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }) - .setTitle(R.string.enable_explore_by_touch_warning_title) - .setMessage(mContext.getString( - R.string.enable_explore_by_touch_warning_message, label)) - .create(); - mEnableTouchExplorationDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); - mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags - |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; - mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true); - mEnableTouchExplorationDialog.show(); - } - } - private int getClientState(UserState userState) { int clientState = 0; if (userState.mIsAccessibilityEnabled) { @@ -1206,16 +1138,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void recreateInternalStateLocked(UserState userState) { populateInstalledAccessibilityServiceLocked(userState); populateEnabledAccessibilityServicesLocked(userState); - populateTouchExplorationGrantedAccessibilityServicesLocked(userState); - populatedEnhancedWebAccessibilityEnabledChangedLocked(userState); handleTouchExplorationEnabledSettingChangedLocked(userState); handleDisplayMagnificationEnabledSettingChangedLocked(userState); handleAccessibilityEnabledSettingChangedLocked(userState); - handleTouchExplorationGrantedAccessibilityServicesChangedLocked(userState); performServiceManagementLocked(userState); - scheduleUpdateInputFilter(userState); scheduleSendStateToClientsLocked(userState); } @@ -1247,61 +1175,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { 0, userState.mUserId) == 1; } - private void handleTouchExplorationGrantedAccessibilityServicesChangedLocked( - UserState userState) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId); - final int serviceCount = userState.mServices.size(); - for (int i = 0; i < serviceCount; i++) { - Service service = userState.mServices.get(i); - tryEnableTouchExplorationLocked(service); - } - } - - private void populatedEnhancedWebAccessibilityEnabledChangedLocked(UserState userState) { - userState.mIsEnhancedWebAccessibilityEnabled = Settings.Secure.getIntForUser( - mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, - 0, userState.mUserId) == 1; - } - private void tryEnableTouchExplorationLocked(Service service) { - if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) { + if (!service.mRequestTouchExplorationMode || !service.canReceiveEventsLocked()) { return; } UserState userState = getUserStateLocked(service.mUserId); - if (userState.mIsTouchExplorationEnabled) { - return; - } - // UI test automation service can always enable it. - if (service.mIsAutomation) { + if (!userState.mIsTouchExplorationEnabled) { Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, service.mUserId); - return; - } - if (service.mResolveInfo.serviceInfo.applicationInfo.targetSdkVersion - <= Build.VERSION_CODES.JELLY_BEAN_MR1) { - // Up to JB-MR1 we had a white list with services that can enable touch - // exploration. When a service is first started we show a dialog to the - // use to get a permission to white list the service. - if (!userState.mTouchExplorationGrantedServices.contains(service.mComponentName)) { - if (mEnableTouchExplorationDialog == null - || (mEnableTouchExplorationDialog != null - && !mEnableTouchExplorationDialog.isShowing())) { - showEnableTouchExplorationDialog(service); - } - } else { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, service.mUserId); - } - } else { - // Starting in JB-MR2 we request a permission to allow a service to enable - // touch exploration and do not care if the service is in the white list. - if (mContext.getPackageManager().checkPermission( - android.Manifest.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE, - service.mComponentName.getPackageName()) == PackageManager.PERMISSION_GRANTED) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, service.mUserId); - } + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, userState.mUserId); } } @@ -1310,53 +1191,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return; } UserState userState = getUserStateLocked(service.mUserId); - if (!userState.mIsTouchExplorationEnabled) { - return; - } - final int serviceCount = userState.mServices.size(); - for (int i = 0; i < serviceCount; i++) { - Service other = userState.mServices.get(i); - if (other != service) { - if (service.mResolveInfo.serviceInfo.applicationInfo.targetSdkVersion - <= Build.VERSION_CODES.JELLY_BEAN_MR1) { - // Up to JB-MR1 we had a white list with services that can enable touch - // exploration. When a service is first started we show a dialog to the - // use to get a permission to white list the service. - if (other.mRequestTouchExplorationMode && - userState.mTouchExplorationGrantedServices.contains( - service.mComponentName)) { - // A white-listed service wants touch exploration, do not disable. - return; - } - } else { - // Starting in JB-MR2 we request a permission to allow a service to enable - // touch exploration and do not care if the service is in the white list. - if (other.mRequestTouchExplorationMode && (service.mIsAutomation - || mContext.getPackageManager().checkPermission( - android.Manifest.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE, - service.mComponentName.getPackageName()) - == PackageManager.PERMISSION_GRANTED)) { - // A service with permission wants touch exploration, do not disable. - return; - } + if (userState.mIsTouchExplorationEnabled) { + final int serviceCount = userState.mServices.size(); + for (int i = 0; i < serviceCount; i++) { + Service other = userState.mServices.get(i); + if (other != service && other.mRequestTouchExplorationMode) { + return; } } + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId); } - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId); } private void tryEnableEnhancedWebAccessibilityLocked(Service service) { - if (!service.canReceiveEventsLocked() || !service.mRequestEnhancedWebAccessibility ) { + if (!service.mRequestEnhancedWebAccessibility || !service.canReceiveEventsLocked()) { return; } UserState userState = getUserStateLocked(service.mUserId); - if (userState.mIsEnhancedWebAccessibilityEnabled) { - return; - } - // Requested and can enabled, do it. - if (service.mRequestEnhancedWebAccessibility - && canEnabledEnhancedWebAccessibility(service)) { + if (!userState.mIsEnhancedWebAccessibilityEnabled) { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 1, userState.mUserId); } @@ -1367,26 +1220,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return; } UserState userState = getUserStateLocked(service.mUserId); - if (!userState.mIsEnhancedWebAccessibilityEnabled) { - return; - } - final int serviceCount = userState.mServices.size(); - for (int i = 0; i < serviceCount; i++) { - Service other = userState.mServices.get(i); - if (other != service && other.mRequestEnhancedWebAccessibility - && canEnabledEnhancedWebAccessibility(other)) { - // One service requests the feature, do not disable. - return; + if (userState.mIsEnhancedWebAccessibilityEnabled) { + final int serviceCount = userState.mServices.size(); + for (int i = 0; i < serviceCount; i++) { + Service other = userState.mServices.get(i); + if (other != service && other.mRequestEnhancedWebAccessibility) { + return; + } } + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0, userState.mUserId); } - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0, service.mUserId); - } - - private boolean canEnabledEnhancedWebAccessibility(Service service) { - return (service.mIsAutomation || mContext.getPackageManager().checkPermission( - android.Manifest.permission.CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY, - service.mComponentName.getPackageName()) == PackageManager.PERMISSION_GRANTED); } @Override @@ -1511,7 +1355,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } break; case MSG_UPDATE_INPUT_FILTER: { UserState userState = (UserState) msg.obj; - updateInputFilter(userState); + updateInputFilter(userState); } break; } } @@ -1683,10 +1527,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS : 0; if (mResolveInfo != null) { - mRequestTouchExplorationMode = (info.flags + String packageName = mResolveInfo.serviceInfo.packageName; + + if (mContext.getPackageManager().checkPermission( + android.Manifest.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE, + packageName) == PackageManager.PERMISSION_GRANTED) { + mRequestTouchExplorationMode = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0; - mRequestEnhancedWebAccessibility = (info.flags + } + + if (mContext.getPackageManager().checkPermission( + android.Manifest.permission.CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY, + packageName) == PackageManager.PERMISSION_GRANTED) { + mRequestEnhancedWebAccessibility = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0; + } } // If this service is up and running we may have to enable touch @@ -2598,9 +2453,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public final Set mEnabledServices = new HashSet(); - public final Set mTouchExplorationGrantedServices = - new HashSet(); - public final SparseArray mInteractionConnections = new SparseArray(); @@ -2638,7 +2490,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mEnabledServices.clear(); mEnabledServices.addAll(userState.mEnabledServices); mTouchExplorationGrantedServices.clear(); - mTouchExplorationGrantedServices.addAll(userState.mTouchExplorationGrantedServices); } public void applyTo(UserState userState) { @@ -2648,8 +2499,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mIsDisplayMagnificationEnabled = mIsDisplayMagnificationEnabled; userState.mEnabledServices.clear(); userState.mEnabledServices.addAll(mEnabledServices); - userState.mTouchExplorationGrantedServices.clear(); - userState.mTouchExplorationGrantedServices.addAll(mTouchExplorationGrantedServices); } public void clear() { @@ -2677,12 +2526,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Uri mEnabledAccessibilityServicesUri = Settings.Secure.getUriFor( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); - private final Uri mTouchExplorationGrantedAccessibilityServicesUri = Settings.Secure - .getUriFor(Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES); - - private final Uri mAccessibilityScriptInjectionUri = Settings.Secure - .getUriFor(Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION); - public AccessibilityContentObserver(Handler handler) { super(handler); } @@ -2696,11 +2539,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver( - mTouchExplorationGrantedAccessibilityServicesUri, - false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver(mAccessibilityScriptInjectionUri, - false, this, UserHandle.USER_ALL); } @Override @@ -2745,23 +2583,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { manageServicesLocked(userState); } } - } else if (mTouchExplorationGrantedAccessibilityServicesUri.equals(uri)) { - synchronized (mLock) { - // We will update when the automation service dies. - if (mUiAutomationService == null) { - UserState userState = getCurrentUserStateLocked(); - populateTouchExplorationGrantedAccessibilityServicesLocked(userState); - handleTouchExplorationGrantedAccessibilityServicesChangedLocked(userState); - } - } - } else if (mAccessibilityScriptInjectionUri.equals(uri)) { - synchronized (mLock) { - // We will update when the automation service dies. - if (mUiAutomationService == null) { - UserState userState = getCurrentUserStateLocked(); - populatedEnhancedWebAccessibilityEnabledChangedLocked(userState); - } - } } } } From 9d44acae80f743b3de8c0ddf208aa5d526511a9b Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Fri, 1 Feb 2013 19:46:20 +0000 Subject: [PATCH 8/8] "Fixing backwards cmpatibility for enabling explore by touch"" This reverts commit d1ed3cea37e4156eab14b7dc8207a520c9eab700 Change-Id: I622ef034526f8f006a5a233e72487b062020e4f6 --- .../AccessibilityServiceInfo.java | 10 +- core/java/android/provider/Settings.java | 1 - core/res/res/values/strings.xml | 23 +- .../providers/settings/DatabaseHelper.java | 13 +- .../AccessibilityManagerService.java | 253 +++++++++++++++--- 5 files changed, 247 insertions(+), 53 deletions(-) diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index ef7186bb6a694..d12a336b13b26 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -150,10 +150,18 @@ public class AccessibilityServiceInfo implements Parcelable { * flag does not guarantee that the device will not be in touch exploration * mode since there may be another enabled service that requested it. *

- * Clients that want to set this flag have to request the + * For accessibility services targeting API version higher than + * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set + * this flag have to request the * {@link android.Manifest.permission#CAN_REQUEST_TOUCH_EXPLORATION_MODE} * permission or the flag will be ignored. *

+ *

+ * Services targeting API version equal to or lower than + * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e. + * the first time they are run, if this flag is specified, a dialog is + * shown to the user to confirm enabling explore by touch. + *

*/ public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index be21fb433f961..3d850cfb022dd 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3351,7 +3351,6 @@ public final class Settings { * * @hide */ - @Deprecated public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES = "touch_exploration_granted_accessibility_services"; diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8f8f32de7c28d..aae509edcdfc6 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -950,8 +950,8 @@ request enhanced web accessibility Allows the hoder to request - enabling of web accessibility enhancements. For example, installing scripts from - Google to make app content more accessible. + enabling of web accessibility enhancements. For example, installing scripts to make + app content more accessible. bind to a text service @@ -2714,6 +2714,25 @@ Voice search + + Enable Explore by Touch? + + + %1$s wants to enable Explore by Touch. + When Explore by Touch is turned on, you can hear or see descriptions of what\'s under + your finger or perform gestures to interact with the tablet. + + + %1$s wants to enable Explore by Touch. + When Explore by Touch is turned on, you can hear or see descriptions of what\'s under + your finger or perform gestures to interact with the phone. + 1 month ago diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 695c2aa708b85..16007c4cacdef 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -1537,18 +1537,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { } if (upgradeVersion == 96) { - // Remove Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES - if (mUserHandle == UserHandle.USER_OWNER) { - db.beginTransaction(); - try { - db.execSQL("DELETE FROM system WHERE name='" - + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES - + "'"); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } + // NOP bump due to a reverted change that some people got on upgrade. upgradeVersion = 97; } diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index f4592f78cf64d..bb040bffd6f2f 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -23,12 +23,15 @@ import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; +import android.app.AlertDialog; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; @@ -67,6 +70,7 @@ import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MagnificationSpec; +import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityManager; @@ -157,6 +161,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private Service mQueryBridge; + private AlertDialog mEnableTouchExplorationDialog; + private AccessibilityInputFilter mInputFilter; private boolean mHasInputFilter; @@ -244,6 +250,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { persistComponentNamesToSettingLocked( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, state.mEnabledServices, userId); + // Update the touch exploration granted services setting. + state.mTouchExplorationGrantedServices.remove(comp); + persistComponentNamesToSettingLocked( + Settings.Secure. + TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + state.mEnabledServices, userId); return; } } @@ -558,6 +570,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mIsDisplayMagnificationEnabled = false; userState.mEnabledServices.clear(); userState.mEnabledServices.add(service); + userState.mTouchExplorationGrantedServices.clear(); + userState.mTouchExplorationGrantedServices.add(service); // Update the internal state. performServiceManagementLocked(userState); scheduleUpdateInputFilter(userState); @@ -831,6 +845,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mEnabledServices); } + private void populateTouchExplorationGrantedAccessibilityServicesLocked(UserState userState) { + populateComponentNamesFromSettingLocked( + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + userState.mUserId, userState.mTouchExplorationGrantedServices); + } + /** * Performs {@link AccessibilityService}s delayed notification. The delay is configurable * and denotes the period after the last event before notifying the service. @@ -1123,6 +1143,54 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private void showEnableTouchExplorationDialog(final Service service) { + String label = service.mResolveInfo.loadLabel( + + mContext.getPackageManager()).toString(); + synchronized (mLock) { + final UserState state = getCurrentUserStateLocked(); + if (state.mIsTouchExplorationEnabled) { + return; + } + if (mEnableTouchExplorationDialog != null + && mEnableTouchExplorationDialog.isShowing()) { + return; + } + mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setPositiveButton(android.R.string.ok, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // The user allowed the service to toggle touch exploration. + state.mTouchExplorationGrantedServices.add(service.mComponentName); + persistComponentNamesToSettingLocked( + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + state.mTouchExplorationGrantedServices, state.mUserId); + // Enable touch exploration. + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, + service.mUserId); + } + }) + .setNegativeButton(android.R.string.cancel, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }) + .setTitle(R.string.enable_explore_by_touch_warning_title) + .setMessage(mContext.getString( + R.string.enable_explore_by_touch_warning_message, label)) + .create(); + mEnableTouchExplorationDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags + |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true); + mEnableTouchExplorationDialog.show(); + } + } + private int getClientState(UserState userState) { int clientState = 0; if (userState.mIsAccessibilityEnabled) { @@ -1138,12 +1206,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void recreateInternalStateLocked(UserState userState) { populateInstalledAccessibilityServiceLocked(userState); populateEnabledAccessibilityServicesLocked(userState); + populateTouchExplorationGrantedAccessibilityServicesLocked(userState); + populatedEnhancedWebAccessibilityEnabledChangedLocked(userState); handleTouchExplorationEnabledSettingChangedLocked(userState); handleDisplayMagnificationEnabledSettingChangedLocked(userState); handleAccessibilityEnabledSettingChangedLocked(userState); + handleTouchExplorationGrantedAccessibilityServicesChangedLocked(userState); performServiceManagementLocked(userState); + scheduleUpdateInputFilter(userState); scheduleSendStateToClientsLocked(userState); } @@ -1175,14 +1247,61 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { 0, userState.mUserId) == 1; } + private void handleTouchExplorationGrantedAccessibilityServicesChangedLocked( + UserState userState) { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId); + final int serviceCount = userState.mServices.size(); + for (int i = 0; i < serviceCount; i++) { + Service service = userState.mServices.get(i); + tryEnableTouchExplorationLocked(service); + } + } + + private void populatedEnhancedWebAccessibilityEnabledChangedLocked(UserState userState) { + userState.mIsEnhancedWebAccessibilityEnabled = Settings.Secure.getIntForUser( + mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, + 0, userState.mUserId) == 1; + } + private void tryEnableTouchExplorationLocked(Service service) { - if (!service.mRequestTouchExplorationMode || !service.canReceiveEventsLocked()) { + if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) { return; } UserState userState = getUserStateLocked(service.mUserId); - if (!userState.mIsTouchExplorationEnabled) { + if (userState.mIsTouchExplorationEnabled) { + return; + } + // UI test automation service can always enable it. + if (service.mIsAutomation) { Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, userState.mUserId); + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, service.mUserId); + return; + } + if (service.mResolveInfo.serviceInfo.applicationInfo.targetSdkVersion + <= Build.VERSION_CODES.JELLY_BEAN_MR1) { + // Up to JB-MR1 we had a white list with services that can enable touch + // exploration. When a service is first started we show a dialog to the + // use to get a permission to white list the service. + if (!userState.mTouchExplorationGrantedServices.contains(service.mComponentName)) { + if (mEnableTouchExplorationDialog == null + || (mEnableTouchExplorationDialog != null + && !mEnableTouchExplorationDialog.isShowing())) { + showEnableTouchExplorationDialog(service); + } + } else { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, service.mUserId); + } + } else { + // Starting in JB-MR2 we request a permission to allow a service to enable + // touch exploration and do not care if the service is in the white list. + if (mContext.getPackageManager().checkPermission( + android.Manifest.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE, + service.mComponentName.getPackageName()) == PackageManager.PERMISSION_GRANTED) { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, service.mUserId); + } } } @@ -1191,25 +1310,53 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return; } UserState userState = getUserStateLocked(service.mUserId); - if (userState.mIsTouchExplorationEnabled) { - final int serviceCount = userState.mServices.size(); - for (int i = 0; i < serviceCount; i++) { - Service other = userState.mServices.get(i); - if (other != service && other.mRequestTouchExplorationMode) { - return; + if (!userState.mIsTouchExplorationEnabled) { + return; + } + final int serviceCount = userState.mServices.size(); + for (int i = 0; i < serviceCount; i++) { + Service other = userState.mServices.get(i); + if (other != service) { + if (service.mResolveInfo.serviceInfo.applicationInfo.targetSdkVersion + <= Build.VERSION_CODES.JELLY_BEAN_MR1) { + // Up to JB-MR1 we had a white list with services that can enable touch + // exploration. When a service is first started we show a dialog to the + // use to get a permission to white list the service. + if (other.mRequestTouchExplorationMode && + userState.mTouchExplorationGrantedServices.contains( + service.mComponentName)) { + // A white-listed service wants touch exploration, do not disable. + return; + } + } else { + // Starting in JB-MR2 we request a permission to allow a service to enable + // touch exploration and do not care if the service is in the white list. + if (other.mRequestTouchExplorationMode && (service.mIsAutomation + || mContext.getPackageManager().checkPermission( + android.Manifest.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE, + service.mComponentName.getPackageName()) + == PackageManager.PERMISSION_GRANTED)) { + // A service with permission wants touch exploration, do not disable. + return; + } } } - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId); } + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId); } private void tryEnableEnhancedWebAccessibilityLocked(Service service) { - if (!service.mRequestEnhancedWebAccessibility || !service.canReceiveEventsLocked()) { + if (!service.canReceiveEventsLocked() || !service.mRequestEnhancedWebAccessibility ) { return; } UserState userState = getUserStateLocked(service.mUserId); - if (!userState.mIsEnhancedWebAccessibilityEnabled) { + if (userState.mIsEnhancedWebAccessibilityEnabled) { + return; + } + // Requested and can enabled, do it. + if (service.mRequestEnhancedWebAccessibility + && canEnabledEnhancedWebAccessibility(service)) { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 1, userState.mUserId); } @@ -1220,17 +1367,26 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return; } UserState userState = getUserStateLocked(service.mUserId); - if (userState.mIsEnhancedWebAccessibilityEnabled) { - final int serviceCount = userState.mServices.size(); - for (int i = 0; i < serviceCount; i++) { - Service other = userState.mServices.get(i); - if (other != service && other.mRequestEnhancedWebAccessibility) { - return; - } - } - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0, userState.mUserId); + if (!userState.mIsEnhancedWebAccessibilityEnabled) { + return; } + final int serviceCount = userState.mServices.size(); + for (int i = 0; i < serviceCount; i++) { + Service other = userState.mServices.get(i); + if (other != service && other.mRequestEnhancedWebAccessibility + && canEnabledEnhancedWebAccessibility(other)) { + // One service requests the feature, do not disable. + return; + } + } + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0, service.mUserId); + } + + private boolean canEnabledEnhancedWebAccessibility(Service service) { + return (service.mIsAutomation || mContext.getPackageManager().checkPermission( + android.Manifest.permission.CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY, + service.mComponentName.getPackageName()) == PackageManager.PERMISSION_GRANTED); } @Override @@ -1355,7 +1511,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } break; case MSG_UPDATE_INPUT_FILTER: { UserState userState = (UserState) msg.obj; - updateInputFilter(userState); + updateInputFilter(userState); } break; } } @@ -1527,21 +1683,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS : 0; if (mResolveInfo != null) { - String packageName = mResolveInfo.serviceInfo.packageName; - - if (mContext.getPackageManager().checkPermission( - android.Manifest.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE, - packageName) == PackageManager.PERMISSION_GRANTED) { - mRequestTouchExplorationMode = (info.flags + mRequestTouchExplorationMode = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0; - } - - if (mContext.getPackageManager().checkPermission( - android.Manifest.permission.CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY, - packageName) == PackageManager.PERMISSION_GRANTED) { - mRequestEnhancedWebAccessibility = (info.flags + mRequestEnhancedWebAccessibility = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0; - } } // If this service is up and running we may have to enable touch @@ -2453,6 +2598,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public final Set mEnabledServices = new HashSet(); + public final Set mTouchExplorationGrantedServices = + new HashSet(); + public final SparseArray mInteractionConnections = new SparseArray(); @@ -2490,6 +2638,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mEnabledServices.clear(); mEnabledServices.addAll(userState.mEnabledServices); mTouchExplorationGrantedServices.clear(); + mTouchExplorationGrantedServices.addAll(userState.mTouchExplorationGrantedServices); } public void applyTo(UserState userState) { @@ -2499,6 +2648,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mIsDisplayMagnificationEnabled = mIsDisplayMagnificationEnabled; userState.mEnabledServices.clear(); userState.mEnabledServices.addAll(mEnabledServices); + userState.mTouchExplorationGrantedServices.clear(); + userState.mTouchExplorationGrantedServices.addAll(mTouchExplorationGrantedServices); } public void clear() { @@ -2526,6 +2677,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Uri mEnabledAccessibilityServicesUri = Settings.Secure.getUriFor( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); + private final Uri mTouchExplorationGrantedAccessibilityServicesUri = Settings.Secure + .getUriFor(Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES); + + private final Uri mAccessibilityScriptInjectionUri = Settings.Secure + .getUriFor(Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION); + public AccessibilityContentObserver(Handler handler) { super(handler); } @@ -2539,6 +2696,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri, false, this, UserHandle.USER_ALL); + contentResolver.registerContentObserver( + mTouchExplorationGrantedAccessibilityServicesUri, + false, this, UserHandle.USER_ALL); + contentResolver.registerContentObserver(mAccessibilityScriptInjectionUri, + false, this, UserHandle.USER_ALL); } @Override @@ -2583,6 +2745,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { manageServicesLocked(userState); } } + } else if (mTouchExplorationGrantedAccessibilityServicesUri.equals(uri)) { + synchronized (mLock) { + // We will update when the automation service dies. + if (mUiAutomationService == null) { + UserState userState = getCurrentUserStateLocked(); + populateTouchExplorationGrantedAccessibilityServicesLocked(userState); + handleTouchExplorationGrantedAccessibilityServicesChangedLocked(userState); + } + } + } else if (mAccessibilityScriptInjectionUri.equals(uri)) { + synchronized (mLock) { + // We will update when the automation service dies. + if (mUiAutomationService == null) { + UserState userState = getCurrentUserStateLocked(); + populatedEnhancedWebAccessibilityEnabledChangedLocked(userState); + } + } } } }