Merge "help image updates" into jb-dev
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 96a6438..a509156 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -1056,7 +1056,12 @@
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 b902550..411f6d0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -361,10 +361,10 @@
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/about/versions/android-4.0.jd b/docs/html/about/versions/android-4.0.jd
index 06b63a0c..99ab9cb 100644
--- a/docs/html/about/versions/android-4.0.jd
+++ b/docs/html/about/versions/android-4.0.jd
@@ -1,4 +1,6 @@
-page.title=Android 4.1 API Overview
+page.title=Android 4.0 Platform
+sdk.platform.version=4.0
+sdk.platform.apiLevel=14
@jd:body
<div id="qv-wrapper">
diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs
index c0c843a..17e08f2 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
new file mode 100644
index 0000000..b4a5c2d
--- /dev/null
+++ b/docs/html/design/media/accessibility_contentdesc.png
Binary files differ
diff --git a/docs/html/design/media/actionbar_drawer.png b/docs/html/design/media/actionbar_drawer.png
new file mode 100644
index 0000000..95e04f5
--- /dev/null
+++ b/docs/html/design/media/actionbar_drawer.png
Binary files differ
diff --git a/docs/html/design/media/dialogs_popups_example.png b/docs/html/design/media/dialogs_popups_example.png
index 2deb00d..c7536f3 100644
--- a/docs/html/design/media/dialogs_popups_example.png
+++ b/docs/html/design/media/dialogs_popups_example.png
Binary files differ
diff --git a/docs/html/design/media/tabs_youtube.png b/docs/html/design/media/tabs_youtube.png
index 69e9268..31c626c 100644
--- a/docs/html/design/media/tabs_youtube.png
+++ b/docs/html/design/media/tabs_youtube.png
Binary files differ
diff --git a/docs/html/design/patterns/accessibility.jd b/docs/html/design/patterns/accessibility.jd
new file mode 100644
index 0000000..a41397f
--- /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/actionbar.jd b/docs/html/design/patterns/actionbar.jd
index 21e8583..80aa77d 100644
--- a/docs/html/design/patterns/actionbar.jd
+++ b/docs/html/design/patterns/actionbar.jd
@@ -234,12 +234,7 @@
</div>
<div class="layout-content-col span-7">
-
- <img src="{@docRoot}design/media/action_bar_pattern_default_tabs.png">
- <div class="figure-caption">
- Default fixed tabs shown in Holo Dark & Light.
- </div>
-
+ <img src="{@docRoot}design/media/actionbar_drawer.png">
</div>
</div>
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 2255bf2..38a29d3 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -39,11 +39,11 @@
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 @@
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 1299574..5c256a3 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 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 @@
* 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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 2918dbc..8c1581c 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.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.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 @@
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 @@
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 @@
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 @@
// Load providers
loadProviders();
+ loadBlacklist();
// Register for Network (Wifi or Mobile) updates
IntentFilter intentFilter = new IntentFilter();
@@ -1110,11 +1124,11 @@
}
}
- 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 @@
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 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 @@
try {
synchronized (mLock) {
requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
- getReceiver(listener));
+ getReceiver(listener, packageName));
}
} catch (SecurityException se) {
throw se;
@@ -1194,7 +1210,9 @@
}
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 @@
try {
synchronized (mLock) {
requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
- getReceiver(intent));
+ getReceiver(intent, packageName));
}
} catch (SecurityException se) {
throw se;
@@ -1270,10 +1288,10 @@
}
}
- 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 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 @@
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 @@
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 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 @@
}
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 @@
", 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 @@
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 @@
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 @@
}
}
- 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 @@
return null;
}
+ if (inBlacklist(packageName)) {
+ return null;
+ }
+
return mLastKnownLocation.get(provider);
}
@@ -1877,6 +1909,10 @@
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 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 @@
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()) {