diff options
17 files changed, 323 insertions, 85 deletions
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index 96a6438768c4..a50915630517 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -1056,7 +1056,12 @@ int unlinklib(const char* dataDir) rc = -errno; goto out; } - + if (chmod(libdir, 0755) < 0) { + ALOGE("cannot chmod dir '%s': %s\n", libdir, strerror(errno)); + unlink(libdir); + rc = -errno; + goto out; + } if (chown(libdir, AID_SYSTEM, AID_SYSTEM) < 0) { ALOGE("cannot chown dir '%s': %s\n", libdir, strerror(errno)); unlink(libdir); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index b902550d1455..411f6d00b0bf 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -361,10 +361,10 @@ class ContextImpl extends Context { return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); }}); - registerService(LOCATION_SERVICE, new StaticServiceFetcher() { - public Object createStaticService() { + registerService(LOCATION_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(LOCATION_SERVICE); - return new LocationManager(ILocationManager.Stub.asInterface(b)); + return new LocationManager(ctx, ILocationManager.Stub.asInterface(b)); }}); registerService(NETWORK_POLICY_SERVICE, new ServiceFetcher() { diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs index c0c843a35936..17e08f2cdef8 100644 --- a/docs/html/design/design_toc.cs +++ b/docs/html/design/design_toc.cs @@ -38,6 +38,7 @@ <li><a href="<?cs var:toroot ?>design/patterns/settings.html">Settings</a></li> <li><a href="<?cs var:toroot ?>design/patterns/help.html">Help</a></li> <li><a href="<?cs var:toroot ?>design/patterns/compatibility.html">Compatibility</a></li> + <li><a href="<?cs var:toroot ?>design/patterns/accessibility.html">Accessibility</a></li> <li><a href="<?cs var:toroot ?>design/patterns/pure-android.html">Pure Android</a></li> </ul> </li> diff --git a/docs/html/design/media/accessibility_contentdesc.png b/docs/html/design/media/accessibility_contentdesc.png Binary files differnew file mode 100644 index 000000000000..b4a5c2dbc8f1 --- /dev/null +++ b/docs/html/design/media/accessibility_contentdesc.png diff --git a/docs/html/design/media/help_better.png b/docs/html/design/media/help_better.png Binary files differnew file mode 100644 index 000000000000..fde5cb297ea6 --- /dev/null +++ b/docs/html/design/media/help_better.png diff --git a/docs/html/design/media/help_cling.png b/docs/html/design/media/help_cling.png Binary files differnew file mode 100644 index 000000000000..c91d18916224 --- /dev/null +++ b/docs/html/design/media/help_cling.png diff --git a/docs/html/design/media/help_dont.png b/docs/html/design/media/help_dont.png Binary files differnew file mode 100644 index 000000000000..a1c9841df7ac --- /dev/null +++ b/docs/html/design/media/help_dont.png diff --git a/docs/html/design/media/help_evenbetter.png b/docs/html/design/media/help_evenbetter.png Binary files differnew file mode 100644 index 000000000000..beb88f8ebb4f --- /dev/null +++ b/docs/html/design/media/help_evenbetter.png diff --git a/docs/html/design/media/help_overflow.png b/docs/html/design/media/help_overflow.png Binary files differnew file mode 100644 index 000000000000..fb2bc0aa5b25 --- /dev/null +++ b/docs/html/design/media/help_overflow.png diff --git a/docs/html/design/media/help_solo_overflow.png b/docs/html/design/media/help_solo_overflow.png Binary files differnew file mode 100644 index 000000000000..9423edef66c0 --- /dev/null +++ b/docs/html/design/media/help_solo_overflow.png diff --git a/docs/html/design/patterns/accessibility.jd b/docs/html/design/patterns/accessibility.jd new file mode 100644 index 000000000000..a41397f05a8c --- /dev/null +++ b/docs/html/design/patterns/accessibility.jd @@ -0,0 +1,61 @@ +page.title=Accessibility +@jd:body + +<p>One of Android's missions is to organize the world's information and make it universally accessible and useful. Accessibility is the measure of how successfully a product can be used by people with varying abilities. Our mission applies to all users-including people with disabilities such as visual impairment, color deficiency, hearing loss, and limited dexterity.</p> +<p><a href="https://www.google.com/#hl=en&q=universal+design&fp=1">Universal design</a> is the practice of making products that are inherently accessible to all users, regardless of ability. The Android design patterns were created in accordance with universal design principles, and following them will help your app meet basic usability standards. Adhering to universal design and enabling Android's accessibility tools will make your app as accessible as possible.</p> +<p>Robust support for accessibility will increase your app's user base. It may also be required for adoption by some organizations.</p> +<p><a href="http://www.google.com/accessibility/">Learn more about Google and accessibility.</a></p> + +<h2 id="tools">Android's Accessibility Tools</h2> +<p>Android includes several features that support access for users with visual impairments; they don't require drastic visual changes to your app.</p> + +<ul> + <li><strong><a href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback">TalkBack</a></strong> is a pre-installed screen reader service provided by Google. It uses spoken feedback to describe the results of actions such as launching an app, and events such as notifications.</li> + <li><strong>Explore by Touch</strong> is a system feature that works with TalkBack, allowing you to touch your device's screen and hear what's under your finger via spoken feedback. This feature is helpful to users with low vision.</li> + <li><strong>Accessibility settings</strong> let you modify your device's display and sound options, such as increasing the text size, changing the speed at which text is spoken, and more.</li> +</ul> + +<p>Some users use hardware or software directional controllers (such as a D-pad, trackball, keyboard) to jump from selection to selection on a screen. They interact with the structure of your app in a linear fashion, similar to 4-way remote control navigation on a television.</p> + +<h2 id="tools">Guidelines</h2> +<p>The Android design principle "I should always know where I am" is key for accessibility concerns. As a user navigates through an application, they need feedback and a mental model of where they are. All users benefit from a strong sense of information hierarchy and an architecture that makes sense. Most users benefit from visual and haptic feedback during their navigation (such as labels, colors, icons, touch feedback) Low vision users benefit from explicit verbal descriptions and large visuals with high contrast.</p> +<p>As you design your app, think about the labels and notations needed to navigate your app by sound. When using Explore by Touch, the user enables an invisible but audible layer of structure in your application. Like any other aspect of app design, this structure can be simple, elegant, and robust. The following are Android's recommended guidelines to enable effective navigation for all users.</p> + +<h4>Make navigation intuitive</h4> +<p>Design well-defined, clear task flows with minimal navigation steps, especially for major user tasks. Make sure those tasks are navigable via focus controls. </p> + +<h4>Use recommended touch target sizes</h4> +<p>48 dp is the recommended touch target size for on screen elements. Read about <a href="{@docRoot}design/style/metrics-grids.html">Android Metrics and Grids</a> to learn about implementation strategies to help most of your users. For certain users, it may be appropriate to use larger touch targets. An example of this is educational apps, where buttons larger than the minimum recommendations are appropriate for children with developing motor skills and people with manual dexterity challenges.</p> + + +<h4>Label visual UI elements meaningfully</h4> +<p>In your wireframes, <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#label-ui">label functional UI components</a> that have no visible text. Those components might be buttons, icons, tabs with icons, and icons with state (like stars). Developers can use the <code><a href="{@docRoot}guide/topics/ui/accessibility/apps.html#label-ui">contentDescription</a></code> attribute to set the label.</p> + +<img src="{@docRoot}design/media/accessibility_contentdesc.png"> + +<h4>Provide alternatives to affordances that time out</h4> +<p>Your app may have icons or controls that disappear after a certain amount of time. For example, five seconds after starting a video, playback controls may fade from the screen.</p> + +<p>Due to the way that TalkBack works, those controls are not read out loud unless they are focused on. If they fade out from the screen quickly, your user may not even be aware that they are available. Therefore, make sure that you are not relying on timed out controls for high priority task flows. (This is a good universal design guideline too.) If the controls enable an important function, make sure that the user can turn on the controls again and/or their function is duplicated elsewhere. You can also change the behavior of your app when accessibility services are turned on. Your developer may be able to make sure that timed-out controls won't disappear.</p> + +<h4>Use standard framework controls or enable TalkBack for custom controls</h4> +<p>Standard Android framework controls work automatically with accessibility services and have ContentDescriptions built in by default.</p> + +<p>An oft-overlooked system control is font size. Users can turn on a system-wide large font size in Settings; using the default system font size in your application will enable the user's preferences in your app as well. To enable system font size in your app, mark text and their associated containers to be measured in <a href="{@docRoot}guide/practices/screens_support.html#screen-independence">scale pixels</a>.</p> + +<p>Also, keep in mind that when users have large fonts enabled or speak a different language than you, their type might be larger than the space you've allotted for it. Read <a href="{@docRoot}design/style/devices-displays.html">Devices and Displays</a> and <a href="http://developer.android.com/guide/practices/screens_support.html">Supporting Multiple Screens</a> for design strategies.</p> + +<p>If you use custom controls, Android has the developer tools in place to allow adherence to the above guidelines and provide meaningful descriptions about the UI. Provide adequate notation on your wireframes and direct your developer to the <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-views">Custom Views</a> documentation.</p> + +<h4>Try it out yourself</h4> +<p>Turn on the TalkBack service in <strong>Settings > Accessibility</strong> and navigate your application using directional controls or eyes-free navigation.</p> + +<h2>Checklist</h2> +<ul> + <li>Make navigation intuitive</li> + <li>Use recommended touch target sizes</li> + <li>Label visual UI elements meaningfully</li> + <li>Provide alternatives to affordances that time out</li> + <li>Use standard framework controls or enable TalkBack for custom controls</li> + <li>Try it out yourself</li> +</ul>
\ No newline at end of file diff --git a/docs/html/design/patterns/help.jd b/docs/html/design/patterns/help.jd index fef7a94b0928..4a92c7bb53f8 100644 --- a/docs/html/design/patterns/help.jd +++ b/docs/html/design/patterns/help.jd @@ -23,17 +23,17 @@ page.title=Help <div class="layout-content-row"> <div class="layout-content-col span-8"> <ol> - <li><h4>High value</h4> + <li><strong>High value</strong> <p>Without it, users wouldn't be able to customize the most frequently-visited Android screen to meet their needs.</p></li> - <li><h4>Available only through a gesture</h4> + <li><strong>Available only through a gesture</strong> <p>Users can't do this through a button or a menu.</p></li> - <li><h4>Atypical for the gesture</h4> + <li><strong>Atypical for the gesture</strong> <p>Many high value functions invoked through a gesture - like scrolling, swiping tabs, and turning pages - are common and yield expected results. Users will be able to discover them in your app, and unsolicited help is unnecessary. But here, the gesture does something unexpected: press and hold doesn't just select an app, it also immediately navigates to the Home Screen and creates a shortcut to the app that can then be dragged around.</p></li> </ol> <p>However, this is an exceptional case. Most functionality doesn't meet all three of these criteria.</p> </div> <div class="layout-content-col span-5"> - <img src="{@docRoot}design/media/swipe_views.png"> + <img src="{@docRoot}design/media/help_cling.png"> <div class="figure-caption"> The first time each user visits the All Apps screen, a semi-transparent overlay appears to teach an important gesture. </div> @@ -47,10 +47,10 @@ page.title=Help <div class="layout-content-row"> <div class="layout-content-col span-7"> - <img src="{@docRoot}design/media/swipe_views2.png"> + <img src="{@docRoot}design/media/help_overflow.png"> </div> <div class="layout-content-col span-6"> - <img src="{@docRoot}design/media/swipe_views2.png"> + <img src="{@docRoot}design/media/help_solo_overflow.png"> <div class="figure-caption"> Even if your screen has no other action overflow items, "Help" should appear there and not be promoted to the action bar. </div> @@ -69,35 +69,31 @@ page.title=Help <p>When someone chooses "Help":</p> <div class="layout-content-row"> - <div class="layout-content-col span-7"> - <h4 class="do-dont-label bad">Don't</h4> - - <p>Present a dialog asking them to choose between help and other options.</p> + <div class="layout-content-col span-4"> + <img src="{@docRoot}design/media/help_dont.png"> </div> - <div class="layout-content-col span-6"> - <img src="{@docRoot}design/media/swipe_views2.png"> + <div class="layout-content-col span-4"> + <img src="{@docRoot}design/media/help_better.png"> </div> + <div class="layout-content-col span-5"> + <img src="{@docRoot}design/media/help_evenbetter.png"> + </div> </div> <div class="layout-content-row"> - <div class="layout-content-col span-7"> + <div class="layout-content-col span-4"> + <h4 class="do-dont-label bad">Don't</h4> + <p>Present a dialog asking them to choose between help and other options.</p> + </div> + <div class="layout-content-col span-4"> <h4 class="do-dont-label good">Better</h4> <p>Immediately launch a web browser with help content. Place other options in a footer.</p> </div> - <div class="layout-content-col span-6"> - <img src="{@docRoot}design/media/swipe_views2.png"> - </div> -</div> - -<div class="layout-content-row"> - <div class="layout-content-col span-7"> + <div class="layout-content-col span-5"> <h4 class="do-dont-label good">Even Better</h4> <p>Build a help screen in your app and offer other options in the action bar.</p> <p>This requires more development work than launching a web browser, but it's a nicer experience for users because they don't leave your app to get the help they need and doesn't require a network connection.</p> </div> - <div class="layout-content-col span-6"> - <img src="{@docRoot}design/media/swipe_views2.png"> - </div> </div> <h2>Principles for Writing On-Screen Help Content</h2> diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index a32e46943d41..f49c4294fb9e 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -22,8 +22,9 @@ import android.net.LocalSocket; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; +import java.io.UTFDataFormatException; import java.nio.charset.Charsets; +import java.nio.charset.ModifiedUtf8; import java.util.ArrayList; /** @@ -75,7 +76,7 @@ public class KeyStore { } public byte[] get(String key) { - return get(getBytes(key)); + return get(getKeyBytes(key)); } private boolean put(byte[] key, byte[] value) { @@ -84,7 +85,7 @@ public class KeyStore { } public boolean put(String key, byte[] value) { - return put(getBytes(key), value); + return put(getKeyBytes(key), value); } private boolean delete(byte[] key) { @@ -93,7 +94,7 @@ public class KeyStore { } public boolean delete(String key) { - return delete(getBytes(key)); + return delete(getKeyBytes(key)); } private boolean contains(byte[] key) { @@ -102,7 +103,7 @@ public class KeyStore { } public boolean contains(String key) { - return contains(getBytes(key)); + return contains(getKeyBytes(key)); } public byte[][] saw(byte[] prefix) { @@ -111,13 +112,13 @@ public class KeyStore { } public String[] saw(String prefix) { - byte[][] values = saw(getBytes(prefix)); + byte[][] values = saw(getKeyBytes(prefix)); if (values == null) { return null; } String[] strings = new String[values.length]; for (int i = 0; i < values.length; ++i) { - strings[i] = toString(values[i]); + strings[i] = toKeyString(values[i]); } return strings; } @@ -133,7 +134,7 @@ public class KeyStore { } public boolean password(String password) { - return password(getBytes(password)); + return password(getPasswordBytes(password)); } public boolean lock() { @@ -147,7 +148,7 @@ public class KeyStore { } public boolean unlock(String password) { - return unlock(getBytes(password)); + return unlock(getPasswordBytes(password)); } public boolean isEmpty() { @@ -161,7 +162,7 @@ public class KeyStore { } public boolean generate(String key) { - return generate(getBytes(key)); + return generate(getKeyBytes(key)); } private boolean importKey(byte[] keyName, byte[] key) { @@ -170,7 +171,7 @@ public class KeyStore { } public boolean importKey(String keyName, byte[] key) { - return importKey(getBytes(keyName), key); + return importKey(getKeyBytes(keyName), key); } private byte[] getPubkey(byte[] key) { @@ -179,7 +180,7 @@ public class KeyStore { } public byte[] getPubkey(String key) { - return getPubkey(getBytes(key)); + return getPubkey(getKeyBytes(key)); } private boolean delKey(byte[] key) { @@ -188,7 +189,7 @@ public class KeyStore { } public boolean delKey(String key) { - return delKey(getBytes(key)); + return delKey(getKeyBytes(key)); } private byte[] sign(byte[] keyName, byte[] data) { @@ -197,7 +198,7 @@ public class KeyStore { } public byte[] sign(String key, byte[] data) { - return sign(getBytes(key), data); + return sign(getKeyBytes(key), data); } private boolean verify(byte[] keyName, byte[] data, byte[] signature) { @@ -206,7 +207,7 @@ public class KeyStore { } public boolean verify(String key, byte[] data, byte[] signature) { - return verify(getBytes(key), data, signature); + return verify(getKeyBytes(key), data, signature); } private boolean grant(byte[] key, byte[] uid) { @@ -215,7 +216,7 @@ public class KeyStore { } public boolean grant(String key, int uid) { - return grant(getBytes(key), Integer.toString(uid).getBytes()); + return grant(getKeyBytes(key), getUidBytes(uid)); } private boolean ungrant(byte[] key, byte[] uid) { @@ -224,7 +225,7 @@ public class KeyStore { } public boolean ungrant(String key, int uid) { - return ungrant(getBytes(key), Integer.toString(uid).getBytes()); + return ungrant(getKeyBytes(key), getUidBytes(uid)); } public int getLastError() { @@ -291,11 +292,34 @@ public class KeyStore { return null; } - private static byte[] getBytes(String string) { - return string.getBytes(Charsets.UTF_8); + /** + * ModifiedUtf8 is used for key encoding to match the + * implementation of NativeCrypto.ENGINE_load_private_key. + */ + private static byte[] getKeyBytes(String string) { + try { + int utfCount = (int) ModifiedUtf8.countBytes(string, false); + byte[] result = new byte[utfCount]; + ModifiedUtf8.encode(result, 0, string); + return result; + } catch (UTFDataFormatException e) { + throw new RuntimeException(e); + } + } + + private static String toKeyString(byte[] bytes) { + try { + return ModifiedUtf8.decode(bytes, new char[bytes.length], 0, bytes.length); + } catch (UTFDataFormatException e) { + throw new RuntimeException(e); + } + } + + private static byte[] getPasswordBytes(String password) { + return password.getBytes(Charsets.UTF_8); } - private static String toString(byte[] bytes) { - return new String(bytes, Charsets.UTF_8); + private static byte[] getUidBytes(int uid) { + return Integer.toString(uid).getBytes(Charsets.UTF_8); } } diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java index 91c56d6761b3..32cd6e2eef32 100755 --- a/keystore/tests/src/android/security/KeyStoreTest.java +++ b/keystore/tests/src/android/security/KeyStoreTest.java @@ -37,7 +37,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { private static final String TEST_PASSWD2 = "87654321"; private static final String TEST_KEYNAME = "test-key"; private static final String TEST_KEYNAME1 = "test-key.1"; - private static final String TEST_KEYNAME2 = "test-key.2"; + private static final String TEST_KEYNAME2 = "test-key\02"; private static final byte[] TEST_KEYVALUE = "test value".getBytes(Charsets.UTF_8); // "Hello, World" in Chinese diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 2255bf2ecaaa..38a29d3a358e 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -39,11 +39,11 @@ interface ILocationManager boolean providerMeetsCriteria(String provider, in Criteria criteria); void requestLocationUpdates(String provider, in Criteria criteria, long minTime, float minDistance, - boolean singleShot, in ILocationListener listener); + boolean singleShot, in ILocationListener listener, String packageName); void requestLocationUpdatesPI(String provider, in Criteria criteria, long minTime, float minDistance, - boolean singleShot, in PendingIntent intent); - void removeUpdates(in ILocationListener listener); - void removeUpdatesPI(in PendingIntent intent); + boolean singleShot, in PendingIntent intent, String packageName); + void removeUpdates(in ILocationListener listener, String packageName); + void removeUpdatesPI(in PendingIntent intent, String packageName); boolean addGpsStatusListener(IGpsStatusListener listener); void removeGpsStatusListener(IGpsStatusListener listener); @@ -54,13 +54,13 @@ interface ILocationManager boolean sendExtraCommand(String provider, String command, inout Bundle extras); void addProximityAlert(double latitude, double longitude, float distance, - long expiration, in PendingIntent intent); + long expiration, in PendingIntent intent, String packageName); void removeProximityAlert(in PendingIntent intent); Bundle getProviderInfo(String provider); boolean isProviderEnabled(String provider); - Location getLastKnownLocation(String provider); + Location getLastKnownLocation(String provider, String packageName); // Used by location providers to tell the location manager when it has a new location. // Passive is true if the location is coming from the passive provider, in which case diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 12995740cb73..5c256a31609f 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -17,6 +17,7 @@ package android.location; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Looper; @@ -160,6 +161,8 @@ public class LocationManager { */ public static final String EXTRA_GPS_ENABLED = "enabled"; + private final Context mContext; + // Map from LocationListeners to their associated ListenerTransport objects private HashMap<LocationListener,ListenerTransport> mListeners = new HashMap<LocationListener,ListenerTransport>(); @@ -260,8 +263,9 @@ public class LocationManager { * right way to create an instance of this class is using the * factory Context.getSystemService. */ - public LocationManager(ILocationManager service) { + public LocationManager(Context context, ILocationManager service) { mService = service; + mContext = context; } private LocationProvider createProvider(String name, Bundle info) { @@ -657,7 +661,8 @@ public class LocationManager { transport = new ListenerTransport(listener, looper); } mListeners.put(listener, transport); - mService.requestLocationUpdates(provider, criteria, minTime, minDistance, singleShot, transport); + mService.requestLocationUpdates(provider, criteria, minTime, minDistance, + singleShot, transport, mContext.getPackageName()); } } catch (RemoteException ex) { Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex); @@ -837,7 +842,8 @@ public class LocationManager { } try { - mService.requestLocationUpdatesPI(provider, criteria, minTime, minDistance, singleShot, intent); + mService.requestLocationUpdatesPI(provider, criteria, minTime, minDistance, singleShot, + intent, mContext.getPackageName()); } catch (RemoteException ex) { Log.e(TAG, "requestLocationUpdates: RemoteException", ex); } @@ -1005,7 +1011,7 @@ public class LocationManager { try { ListenerTransport transport = mListeners.remove(listener); if (transport != null) { - mService.removeUpdates(transport); + mService.removeUpdates(transport, mContext.getPackageName()); } } catch (RemoteException ex) { Log.e(TAG, "removeUpdates: DeadObjectException", ex); @@ -1028,7 +1034,7 @@ public class LocationManager { Log.d(TAG, "removeUpdates: intent = " + intent); } try { - mService.removeUpdatesPI(intent); + mService.removeUpdatesPI(intent, mContext.getPackageName()); } catch (RemoteException ex) { Log.e(TAG, "removeUpdates: RemoteException", ex); } @@ -1087,7 +1093,7 @@ public class LocationManager { } try { mService.addProximityAlert(latitude, longitude, radius, - expiration, intent); + expiration, intent, mContext.getPackageName()); } catch (RemoteException ex) { Log.e(TAG, "addProximityAlert: RemoteException", ex); } @@ -1153,7 +1159,7 @@ public class LocationManager { throw new IllegalArgumentException("provider==null"); } try { - return mService.getLastKnownLocation(provider); + return mService.getLastKnownLocation(provider, mContext.getPackageName()); } catch (RemoteException ex) { Log.e(TAG, "getLastKnowLocation: RemoteException", ex); return null; diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 2918dbcdb635..8c1581cf1297 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -32,6 +32,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.content.res.Resources; +import android.database.ContentObserver; import android.database.Cursor; import android.location.Address; import android.location.Criteria; @@ -79,6 +80,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Observable; @@ -109,6 +112,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private static final String INSTALL_LOCATION_PROVIDER = android.Manifest.permission.INSTALL_LOCATION_PROVIDER; + private static final String BLACKLIST_CONFIG_NAME = "locationPackagePrefixBlacklist"; + private static final String WHITELIST_CONFIG_NAME = "locationPackagePrefixWhitelist"; + // Location Providers may sometimes deliver location updates // slightly faster that requested - provide grace period so // we don't unnecessarily filter events that are otherwise on @@ -193,6 +199,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE; + // for prefix blacklist + private String[] mWhitelist = new String[0]; + private String[] mBlacklist = new String[0]; + // for Settings change notification private ContentQueryMap mSettings; @@ -205,20 +215,23 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final PendingIntent mPendingIntent; final Object mKey; final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>(); + final String mPackageName; int mPendingBroadcasts; String mRequiredPermissions; - Receiver(ILocationListener listener) { + Receiver(ILocationListener listener, String packageName) { mListener = listener; mPendingIntent = null; mKey = listener.asBinder(); + mPackageName = packageName; } - Receiver(PendingIntent intent) { + Receiver(PendingIntent intent, String packageName) { mPendingIntent = intent; mListener = null; mKey = intent; + mPackageName = packageName; } @Override @@ -601,6 +614,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Load providers loadProviders(); + loadBlacklist(); // Register for Network (Wifi or Mobile) updates IntentFilter intentFilter = new IntentFilter(); @@ -1110,11 +1124,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - private Receiver getReceiver(ILocationListener listener) { + private Receiver getReceiver(ILocationListener listener, String packageName) { IBinder binder = listener.asBinder(); Receiver receiver = mReceivers.get(binder); if (receiver == null) { - receiver = new Receiver(listener); + receiver = new Receiver(listener, packageName); mReceivers.put(binder, receiver); try { @@ -1129,10 +1143,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return receiver; } - private Receiver getReceiver(PendingIntent intent) { + private Receiver getReceiver(PendingIntent intent, String packageName) { Receiver receiver = mReceivers.get(intent); if (receiver == null) { - receiver = new Receiver(intent); + receiver = new Receiver(intent, packageName); mReceivers.put(intent, receiver); } return receiver; @@ -1157,7 +1171,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } public void requestLocationUpdates(String provider, Criteria criteria, - long minTime, float minDistance, boolean singleShot, ILocationListener listener) { + long minTime, float minDistance, boolean singleShot, ILocationListener listener, + String packageName) { + checkPackageName(Binder.getCallingUid(), packageName); if (criteria != null) { // FIXME - should we consider using multiple providers simultaneously // rather than only the best one? @@ -1170,7 +1186,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run try { synchronized (mLock) { requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot, - getReceiver(listener)); + getReceiver(listener, packageName)); } } catch (SecurityException se) { throw se; @@ -1194,7 +1210,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } public void requestLocationUpdatesPI(String provider, Criteria criteria, - long minTime, float minDistance, boolean singleShot, PendingIntent intent) { + long minTime, float minDistance, boolean singleShot, PendingIntent intent, + String packageName) { + checkPackageName(Binder.getCallingUid(), packageName); validatePendingIntent(intent); if (criteria != null) { // FIXME - should we consider using multiple providers simultaneously @@ -1208,7 +1226,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run try { synchronized (mLock) { requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot, - getReceiver(intent)); + getReceiver(intent, packageName)); } } catch (SecurityException se) { throw se; @@ -1270,10 +1288,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - public void removeUpdates(ILocationListener listener) { + public void removeUpdates(ILocationListener listener, String packageName) { try { synchronized (mLock) { - removeUpdatesLocked(getReceiver(listener)); + removeUpdatesLocked(getReceiver(listener, packageName)); } } catch (SecurityException se) { throw se; @@ -1284,10 +1302,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - public void removeUpdatesPI(PendingIntent intent) { + public void removeUpdatesPI(PendingIntent intent, String packageName) { try { synchronized (mLock) { - removeUpdatesLocked(getReceiver(intent)); + removeUpdatesLocked(getReceiver(intent, packageName)); } } catch (SecurityException se) { throw se; @@ -1446,15 +1464,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final long mExpiration; final PendingIntent mIntent; final Location mLocation; + final String mPackageName; public ProximityAlert(int uid, double latitude, double longitude, - float radius, long expiration, PendingIntent intent) { + float radius, long expiration, PendingIntent intent, String packageName) { mUid = uid; mLatitude = latitude; mLongitude = longitude; mRadius = radius; mExpiration = expiration; mIntent = intent; + mPackageName = packageName; mLocation = new Location(""); mLocation.setLatitude(latitude); @@ -1522,6 +1542,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run PendingIntent intent = alert.getIntent(); long expiration = alert.getExpiration(); + if (inBlacklist(alert.mPackageName)) { + continue; + } + if ((expiration == -1) || (now <= expiration)) { boolean entered = mProximitiesEntered.contains(alert); boolean inProximity = @@ -1632,11 +1656,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } public void addProximityAlert(double latitude, double longitude, - float radius, long expiration, PendingIntent intent) { + float radius, long expiration, PendingIntent intent, String packageName) { validatePendingIntent(intent); try { synchronized (mLock) { - addProximityAlertLocked(latitude, longitude, radius, expiration, intent); + addProximityAlertLocked(latitude, longitude, radius, expiration, intent, + packageName); } } catch (SecurityException se) { throw se; @@ -1648,7 +1673,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private void addProximityAlertLocked(double latitude, double longitude, - float radius, long expiration, PendingIntent intent) { + float radius, long expiration, PendingIntent intent, String packageName) { if (LOCAL_LOGV) { Slog.v(TAG, "addProximityAlert: latitude = " + latitude + ", longitude = " + longitude + @@ -1656,6 +1681,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run ", intent = " + intent); } + checkPackageName(Binder.getCallingUid(), packageName); + // Require ability to access all providers for now if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) || !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) { @@ -1666,12 +1693,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run expiration += System.currentTimeMillis(); } ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(), - latitude, longitude, radius, expiration, intent); + latitude, longitude, radius, expiration, intent, packageName); mProximityAlerts.put(intent, alert); if (mProximityReceiver == null) { mProximityListener = new ProximityListener(); - mProximityReceiver = new Receiver(mProximityListener); + mProximityReceiver = new Receiver(mProximityListener, packageName); for (int i = mProviders.size() - 1; i >= 0; i--) { LocationProviderInterface provider = mProviders.get(i); @@ -1787,13 +1814,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return isAllowedBySettingsLocked(provider); } - public Location getLastKnownLocation(String provider) { + public Location getLastKnownLocation(String provider, String packageName) { if (LOCAL_LOGV) { Slog.v(TAG, "getLastKnownLocation: " + provider); } try { synchronized (mLock) { - return _getLastKnownLocationLocked(provider); + return _getLastKnownLocationLocked(provider, packageName); } } catch (SecurityException se) { throw se; @@ -1803,8 +1830,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - private Location _getLastKnownLocationLocked(String provider) { + private Location _getLastKnownLocationLocked(String provider, String packageName) { checkPermissionsSafe(provider, null); + checkPackageName(Binder.getCallingUid(), packageName); LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { @@ -1815,6 +1843,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return null; } + if (inBlacklist(packageName)) { + return null; + } + return mLastKnownLocation.get(provider); } @@ -1877,6 +1909,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run Receiver receiver = r.mReceiver; boolean receiverDead = false; + if (inBlacklist(receiver.mPackageName)) { + continue; + } + Location lastLoc = r.mLastFixBroadcast; if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) { if (lastLoc == null) { @@ -2315,6 +2351,113 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + public class BlacklistObserver extends ContentObserver { + public BlacklistObserver(Handler handler) { + super(handler); + } + @Override + public void onChange(boolean selfChange) { + reloadBlacklist(); + } + } + + private void loadBlacklist() { + // Register for changes + BlacklistObserver observer = new BlacklistObserver(mLocationHandler); + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + BLACKLIST_CONFIG_NAME), false, observer); + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + WHITELIST_CONFIG_NAME), false, observer); + reloadBlacklist(); + } + + private void reloadBlacklist() { + String blacklist[] = getStringArray(BLACKLIST_CONFIG_NAME); + String whitelist[] = getStringArray(WHITELIST_CONFIG_NAME); + synchronized (mLock) { + mWhitelist = whitelist; + Slog.i(TAG, "whitelist: " + arrayToString(mWhitelist)); + mBlacklist = blacklist; + Slog.i(TAG, "blacklist: " + arrayToString(mBlacklist)); + } + } + + private static String arrayToString(String[] array) { + StringBuilder s = new StringBuilder(); + s.append('['); + boolean first = true; + for (String a : array) { + if (!first) s.append(','); + first = false; + s.append(a); + } + s.append(']'); + return s.toString(); + } + + private String[] getStringArray(String key) { + String flatString = Settings.Secure.getString(mContext.getContentResolver(), key); + if (flatString == null) { + return new String[0]; + } + String[] splitStrings = flatString.split(","); + ArrayList<String> result = new ArrayList<String>(); + for (String pkg : splitStrings) { + pkg = pkg.trim(); + if (pkg.isEmpty()) { + continue; + } + result.add(pkg); + } + return result.toArray(new String[result.size()]); + } + + /** + * Return true if in blacklist, and not in whitelist. + */ + private boolean inBlacklist(String packageName) { + synchronized (mLock) { + for (String black : mBlacklist) { + if (packageName.startsWith(black)) { + if (inWhitelist(packageName)) { + continue; + } else { + if (LOCAL_LOGV) Log.d(TAG, "dropping location (blacklisted): " + + packageName + " matches " + black); + return true; + } + } + } + } + return false; + } + + /** + * Return true if any of packages are in whitelist + */ + private boolean inWhitelist(String pkg) { + synchronized (mLock) { + for (String white : mWhitelist) { + if (pkg.startsWith(white)) return true; + } + } + return false; + } + + private void checkPackageName(int uid, String packageName) { + if (packageName == null) { + throw new SecurityException("packageName cannot be null"); + } + String[] packages = mPackageManager.getPackagesForUid(uid); + if (packages == null) { + throw new SecurityException("invalid UID " + uid); + } + for (String pkg : packages) { + if (packageName.equals(pkg)) return; + } + throw new SecurityException("invalid package name"); + } + private void log(String log) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.d(TAG, log); @@ -2346,6 +2489,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run j.getValue().dump(pw, " "); } } + pw.println(" Package blacklist:" + arrayToString(mBlacklist)); + pw.println(" Package whitelist:" + arrayToString(mWhitelist)); pw.println(" Records by Provider:"); for (Map.Entry<String, ArrayList<UpdateRecord>> i : mRecordsByProvider.entrySet()) { |