summaryrefslogtreecommitdiffstats
path: root/services/java/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android')
-rw-r--r--services/java/com/android/server/BackupManagerService.java2
-rw-r--r--services/java/com/android/server/Installer.java49
-rw-r--r--services/java/com/android/server/LocationManagerService.java35
-rw-r--r--services/java/com/android/server/MasterClearReceiver.java6
-rw-r--r--services/java/com/android/server/PackageManagerService.java69
-rw-r--r--services/java/com/android/server/WindowManagerService.java8
-rw-r--r--services/java/com/android/server/location/GeocoderProxy.java105
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java1475
-rw-r--r--services/java/com/android/server/location/GpsXtraDownloader.java171
-rw-r--r--services/java/com/android/server/location/LocationProviderInterface.java52
-rw-r--r--services/java/com/android/server/location/LocationProviderProxy.java385
-rw-r--r--services/java/com/android/server/location/MockProvider.java206
-rw-r--r--services/java/com/android/server/location/PassiveProvider.java142
-rw-r--r--services/java/com/android/server/status/StatusBarPolicy.java18
14 files changed, 2674 insertions, 49 deletions
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index d67dde0..2c388ee 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -421,7 +421,7 @@ class BackupManagerService extends IBackupManager.Stub {
Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
// If Encrypted file systems is enabled or disabled, this call will return the
// correct directory.
- mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
+ mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
mBaseStateDir.mkdirs();
mDataDir = Environment.getDownloadCacheDirectory();
diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java
index 2eaa58c..1f34eba 100644
--- a/services/java/com/android/server/Installer.java
+++ b/services/java/com/android/server/Installer.java
@@ -166,11 +166,17 @@ class Installer {
}
}
- public int install(String name, int uid, int gid) {
+ public int install(String name, boolean useEncryptedFilesystem, int uid, int gid) {
StringBuilder builder = new StringBuilder("install");
builder.append(' ');
builder.append(name);
builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
+ builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(gid);
@@ -203,33 +209,57 @@ class Installer {
return execute(builder.toString());
}
- public int remove(String name) {
+ public int remove(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("remove");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
- public int rename(String oldname, String newname) {
+ public int rename(String oldname, String newname, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rename");
builder.append(' ');
builder.append(oldname);
builder.append(' ');
builder.append(newname);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
- public int deleteCacheFiles(String name) {
+ public int deleteCacheFiles(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rmcache");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
- public int clearUserData(String name) {
+ public int clearUserData(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rmuserdata");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
return execute(builder.toString());
}
@@ -263,7 +293,7 @@ class Installer {
}
public int getSizeInfo(String pkgName, String apkPath,
- String fwdLockApkPath, PackageStats pStats) {
+ String fwdLockApkPath, PackageStats pStats, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
@@ -271,6 +301,13 @@ class Installer {
builder.append(apkPath);
builder.append(' ');
builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
+
String s = transaction(builder.toString());
String res[] = s.split(" ");
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 65f4194..33b0e81 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,17 +16,6 @@
package com.android.server;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Observable;
-import java.util.Observer;
-import java.util.Set;
-
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -50,7 +39,6 @@ import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
@@ -68,12 +56,25 @@ import android.util.Log;
import android.util.Slog;
import android.util.PrintWriterPrinter;
-import com.android.internal.location.GeocoderProxy;
-import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.GpsNetInitiatedHandler;
-import com.android.internal.location.LocationProviderProxy;
-import com.android.internal.location.MockProvider;
-import com.android.internal.location.PassiveProvider;
+
+import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationProviderInterface;
+import com.android.server.location.LocationProviderProxy;
+import com.android.server.location.MockProvider;
+import com.android.server.location.PassiveProvider;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Set;
/**
* The service class that manages LocationProviders and issues location
diff --git a/services/java/com/android/server/MasterClearReceiver.java b/services/java/com/android/server/MasterClearReceiver.java
index 27a8a74..4d04cee 100644
--- a/services/java/com/android/server/MasterClearReceiver.java
+++ b/services/java/com/android/server/MasterClearReceiver.java
@@ -39,7 +39,11 @@ public class MasterClearReceiver extends BroadcastReceiver {
try {
Slog.w(TAG, "!!! FACTORY RESET !!!");
- RecoverySystem.rebootWipeUserData(context);
+ if (intent.hasExtra("enableEFS")) {
+ RecoverySystem.rebootToggleEFS(context, intent.getBooleanExtra("enableEFS", false));
+ } else {
+ RecoverySystem.rebootWipeUserData(context);
+ }
Log.wtf(TAG, "Still running after master clear?!");
} catch (IOException e) {
Slog.e(TAG, "Can't perform master clear/factory reset", e);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index df93215..599023c 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -150,6 +150,8 @@ class PackageManagerService extends IPackageManager.Stub {
private static final boolean GET_CERTIFICATES = true;
+ private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
+
private static final int REMOVE_EVENTS =
FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM;
private static final int ADD_EVENTS =
@@ -204,6 +206,10 @@ class PackageManagerService extends IPackageManager.Stub {
// This is where all application persistent data goes.
final File mAppDataDir;
+ // If Encrypted File System feature is enabled, all application persistent data
+ // should go here instead.
+ final File mSecureAppDataDir;
+
// This is the object monitoring the framework dir.
final FileObserver mFrameworkInstallObserver;
@@ -768,6 +774,7 @@ class PackageManagerService extends IPackageManager.Stub {
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
+ mSecureAppDataDir = new File(dataDir, "secure/data");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
if (mInstaller == null) {
@@ -777,6 +784,7 @@ class PackageManagerService extends IPackageManager.Stub {
File miscDir = new File(dataDir, "misc");
miscDir.mkdirs();
mAppDataDir.mkdirs();
+ mSecureAppDataDir.mkdirs();
mDrmAppPrivateInstallDir.mkdirs();
}
@@ -937,7 +945,9 @@ class PackageManagerService extends IPackageManager.Stub {
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
if (mInstaller != null) {
- mInstaller.remove(ps.name);
+ // XXX how to set useEncryptedFSDir for packages that
+ // are not encrypted?
+ mInstaller.remove(ps.name, true);
}
}
}
@@ -1020,7 +1030,8 @@ class PackageManagerService extends IPackageManager.Stub {
void cleanupInstallFailedPackage(PackageSetting ps) {
Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
if (mInstaller != null) {
- int retCode = mInstaller.remove(ps.name);
+ boolean useSecureFS = useEncryptedFilesystemForPackage(ps.pkg);
+ int retCode = mInstaller.remove(ps.name, useSecureFS);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data directory for package: "
+ ps.name + ", retcode=" + retCode);
@@ -1718,6 +1729,7 @@ class PackageManagerService extends IPackageManager.Stub {
static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
if (pi1.icon != pi2.icon) return false;
+ if (pi1.logo != pi2.logo) return false;
if (pi1.protectionLevel != pi2.protectionLevel) return false;
if (!compareStrings(pi1.name, pi2.name)) return false;
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
@@ -2743,6 +2755,11 @@ class PackageManagerService extends IPackageManager.Stub {
return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
}
+
+ private static boolean useEncryptedFilesystemForPackage(PackageParser.Package pkg) {
+ return Environment.isEncryptedFilesystemEnabled() &&
+ ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_NEVER_ENCRYPT) == 0);
+ }
private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) {
if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -2760,7 +2777,14 @@ class PackageManagerService extends IPackageManager.Stub {
}
private File getDataPathForPackage(PackageParser.Package pkg) {
- return new File(mAppDataDir, pkg.packageName);
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
+ File dataPath;
+ if (useEncryptedFSDir) {
+ dataPath = new File(mSecureAppDataDir, pkg.packageName);
+ } else {
+ dataPath = new File(mAppDataDir, pkg.packageName);
+ }
+ return dataPath;
}
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
@@ -3111,6 +3135,7 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
// This is a normal package, need to make its data directory.
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
dataPath = getDataPathForPackage(pkg);
boolean uidError = false;
@@ -3127,7 +3152,7 @@ class PackageManagerService extends IPackageManager.Stub {
// If this is a system app, we can at least delete its
// current data so the application will still work.
if (mInstaller != null) {
- int ret = mInstaller.remove(pkgName);
+ int ret = mInstaller.remove(pkgName, useEncryptedFSDir);
if (ret >= 0) {
// Old data gone!
String msg = "System package " + pkg.packageName
@@ -3138,7 +3163,7 @@ class PackageManagerService extends IPackageManager.Stub {
recovered = true;
// And now re-install the app.
- ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+ ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
if (ret == -1) {
// Ack should not happen!
@@ -3178,7 +3203,7 @@ class PackageManagerService extends IPackageManager.Stub {
Log.v(TAG, "Want this data dir: " + dataPath);
//invoke installer to do the actual installation
if (mInstaller != null) {
- int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+ int ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
if(ret < 0) {
// Error from installer
@@ -6209,8 +6234,9 @@ class PackageManagerService extends IPackageManager.Stub {
deletedPs = mSettings.mPackages.get(packageName);
}
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
if (mInstaller != null) {
- int retCode = mInstaller.remove(packageName);
+ int retCode = mInstaller.remove(packageName, useEncryptedFSDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data or cache directory for package: "
+ packageName + ", retcode=" + retCode);
@@ -6451,6 +6477,7 @@ class PackageManagerService extends IPackageManager.Stub {
p = ps.pkg;
}
}
+ boolean useEncryptedFSDir = false;
if(!dataOnly) {
//need to check this only for fully installed applications
@@ -6463,9 +6490,10 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
+ useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
}
if (mInstaller != null) {
- int retCode = mInstaller.clearUserData(packageName);
+ int retCode = mInstaller.clearUserData(packageName, useEncryptedFSDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -6516,8 +6544,9 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
if (mInstaller != null) {
- int retCode = mInstaller.deleteCacheFiles(packageName);
+ int retCode = mInstaller.deleteCacheFiles(packageName, useEncryptedFSDir);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -6579,9 +6608,10 @@ class PackageManagerService extends IPackageManager.Stub {
}
publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null;
}
+ boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
if (mInstaller != null) {
int res = mInstaller.getSizeInfo(packageName, p.mPath,
- publicSrcDir, pStats);
+ publicSrcDir, pStats, useEncryptedFSDir);
if (res < 0) {
return false;
} else {
@@ -7092,6 +7122,12 @@ class PackageManagerService extends IPackageManager.Stub {
pw.print(" supportsScreens=[");
boolean first = true;
if ((ps.pkg.applicationInfo.flags &
+ ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+ if (!first) pw.print(", ");
+ first = false;
+ pw.print("small");
+ }
+ if ((ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
if (!first) pw.print(", ");
first = false;
@@ -7104,10 +7140,10 @@ class PackageManagerService extends IPackageManager.Stub {
pw.print("large");
}
if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+ ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
if (!first) pw.print(", ");
first = false;
- pw.print("small");
+ pw.print("xlarge");
}
if ((ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
@@ -7631,7 +7667,8 @@ class PackageManagerService extends IPackageManager.Stub {
this.pkgFlags = pkgFlags & (
ApplicationInfo.FLAG_SYSTEM |
ApplicationInfo.FLAG_FORWARD_LOCK |
- ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+ ApplicationInfo.FLAG_EXTERNAL_STORAGE |
+ ApplicationInfo.FLAG_NEVER_ENCRYPT);
}
}
@@ -7898,11 +7935,17 @@ class PackageManagerService extends IPackageManager.Stub {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
// TODO(oam): This secure dir creation needs to be moved somewhere else (later)
+ File systemSecureDir = new File(dataDir, "secure/system");
systemDir.mkdirs();
+ systemSecureDir.mkdirs();
FileUtils.setPermissions(systemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
+ FileUtils.setPermissions(systemSecureDir.toString(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG
+ |FileUtils.S_IROTH|FileUtils.S_IXOTH,
+ -1, -1);
mSettingsFilename = new File(systemDir, "packages.xml");
mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");
mPackageListFilename = new File(systemDir, "packages.list");
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index be5cc7b..a1c08fd 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -4900,8 +4900,12 @@ public class WindowManagerService extends IWindowManager.Stub
mScreenLayout = Configuration.SCREENLAYOUT_SIZE_SMALL
| Configuration.SCREENLAYOUT_LONG_NO;
} else {
- // Is this a large screen?
- if (longSize > 640 && shortSize >= 480) {
+ // What size is this screen screen?
+ if (longSize >= 800 && shortSize >= 600) {
+ // SVGA or larger screens at medium density are the point
+ // at which we consider it to be an extra large screen.
+ mScreenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ } else if (longSize >= 640 && shortSize >= 480) {
// VGA or larger screens at medium density are the point
// at which we consider it to be a large screen.
mScreenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE;
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
new file mode 100644
index 0000000..3c05da2
--- /dev/null
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.Address;
+import android.location.GeocoderParams;
+import android.location.IGeocodeProvider;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * A class for proxying IGeocodeProvider implementations.
+ *
+ * {@hide}
+ */
+public class GeocoderProxy {
+
+ private static final String TAG = "GeocoderProxy";
+
+ private final Context mContext;
+ private final Intent mIntent;
+ private final Connection mServiceConnection = new Connection();
+ private IGeocodeProvider mProvider;
+
+ public GeocoderProxy(Context context, String serviceName) {
+ mContext = context;
+ mIntent = new Intent(serviceName);
+ mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ private class Connection implements ServiceConnection {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "onServiceConnected " + className);
+ synchronized (this) {
+ mProvider = IGeocodeProvider.Stub.asInterface(service);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "onServiceDisconnected " + className);
+ synchronized (this) {
+ mProvider = null;
+ }
+ }
+ }
+
+ public String getFromLocation(double latitude, double longitude, int maxResults,
+ GeocoderParams params, List<Address> addrs) {
+ IGeocodeProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.getFromLocation(latitude, longitude, maxResults,
+ params, addrs);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocation failed", e);
+ }
+ }
+ return "Service not Available";
+ }
+
+ public String getFromLocationName(String locationName,
+ double lowerLeftLatitude, double lowerLeftLongitude,
+ double upperRightLatitude, double upperRightLongitude, int maxResults,
+ GeocoderParams params, List<Address> addrs) {
+ IGeocodeProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.getFromLocationName(locationName, lowerLeftLatitude,
+ lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+ maxResults, params, addrs);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocationName failed", e);
+ }
+ }
+ return "Service not Available";
+ }
+}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
new file mode 100755
index 0000000..19c9018
--- /dev/null
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -0,0 +1,1475 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.location.Criteria;
+import android.location.IGpsStatusListener;
+import android.location.IGpsStatusProvider;
+import android.location.ILocationManager;
+import android.location.INetInitiatedListener;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.SntpClient;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.Phone;
+import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.StringBufferInputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Properties;
+import java.util.Map.Entry;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A GPS implementation of LocationProvider used by LocationManager.
+ *
+ * {@hide}
+ */
+public class GpsLocationProvider implements LocationProviderInterface {
+
+ private static final String TAG = "GpsLocationProvider";
+
+ private static final boolean DEBUG = false;
+ private static final boolean VERBOSE = false;
+
+ // these need to match GpsPositionMode enum in gps.h
+ private static final int GPS_POSITION_MODE_STANDALONE = 0;
+ private static final int GPS_POSITION_MODE_MS_BASED = 1;
+ private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
+
+ // these need to match GpsPositionRecurrence enum in gps.h
+ private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
+ private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
+
+ // these need to match GpsStatusValue defines in gps.h
+ private static final int GPS_STATUS_NONE = 0;
+ private static final int GPS_STATUS_SESSION_BEGIN = 1;
+ private static final int GPS_STATUS_SESSION_END = 2;
+ private static final int GPS_STATUS_ENGINE_ON = 3;
+ private static final int GPS_STATUS_ENGINE_OFF = 4;
+
+ // these need to match GpsApgsStatusValue defines in gps.h
+ /** AGPS status event values. */
+ private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
+ private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
+ private static final int GPS_AGPS_DATA_CONNECTED = 3;
+ private static final int GPS_AGPS_DATA_CONN_DONE = 4;
+ private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
+
+ // these need to match GpsLocationFlags enum in gps.h
+ private static final int LOCATION_INVALID = 0;
+ private static final int LOCATION_HAS_LAT_LONG = 1;
+ private static final int LOCATION_HAS_ALTITUDE = 2;
+ private static final int LOCATION_HAS_SPEED = 4;
+ private static final int LOCATION_HAS_BEARING = 8;
+ private static final int LOCATION_HAS_ACCURACY = 16;
+
+// IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
+ private static final int GPS_DELETE_EPHEMERIS = 0x0001;
+ private static final int GPS_DELETE_ALMANAC = 0x0002;
+ private static final int GPS_DELETE_POSITION = 0x0004;
+ private static final int GPS_DELETE_TIME = 0x0008;
+ private static final int GPS_DELETE_IONO = 0x0010;
+ private static final int GPS_DELETE_UTC = 0x0020;
+ private static final int GPS_DELETE_HEALTH = 0x0040;
+ private static final int GPS_DELETE_SVDIR = 0x0080;
+ private static final int GPS_DELETE_SVSTEER = 0x0100;
+ private static final int GPS_DELETE_SADATA = 0x0200;
+ private static final int GPS_DELETE_RTI = 0x0400;
+ private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
+ private static final int GPS_DELETE_ALL = 0xFFFF;
+
+ // The GPS_CAPABILITY_* flags must match the values in gps.h
+ private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
+ private static final int GPS_CAPABILITY_MSB = 0x0000002;
+ private static final int GPS_CAPABILITY_MSA = 0x0000004;
+ private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
+
+
+ // these need to match AGpsType enum in gps.h
+ private static final int AGPS_TYPE_SUPL = 1;
+ private static final int AGPS_TYPE_C2K = 2;
+
+ // for mAGpsDataConnectionState
+ private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
+ private static final int AGPS_DATA_CONNECTION_OPENING = 1;
+ private static final int AGPS_DATA_CONNECTION_OPEN = 2;
+
+ // Handler messages
+ private static final int CHECK_LOCATION = 1;
+ private static final int ENABLE = 2;
+ private static final int ENABLE_TRACKING = 3;
+ private static final int UPDATE_NETWORK_STATE = 4;
+ private static final int INJECT_NTP_TIME = 5;
+ private static final int DOWNLOAD_XTRA_DATA = 6;
+ private static final int UPDATE_LOCATION = 7;
+ private static final int ADD_LISTENER = 8;
+ private static final int REMOVE_LISTENER = 9;
+
+ private static final String PROPERTIES_FILE = "/etc/gps.conf";
+
+ private int mLocationFlags = LOCATION_INVALID;
+
+ // current status
+ private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
+
+ // time for last status update
+ private long mStatusUpdateTime = SystemClock.elapsedRealtime();
+
+ // turn off GPS fix icon if we haven't received a fix in 10 seconds
+ private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
+
+ // stop trying if we do not receive a fix within 60 seconds
+ private static final int NO_FIX_TIMEOUT = 60 * 1000;
+
+ // true if we are enabled
+ private volatile boolean mEnabled;
+
+ // true if we have network connectivity
+ private boolean mNetworkAvailable;
+
+ // flags to trigger NTP or XTRA data download when network becomes available
+ // initialized to true so we do NTP and XTRA when the network comes up after booting
+ private boolean mInjectNtpTimePending = true;
+ private boolean mDownloadXtraDataPending = true;
+
+ // true if GPS is navigating
+ private boolean mNavigating;
+
+ // true if GPS engine is on
+ private boolean mEngineOn;
+
+ // requested frequency of fixes, in milliseconds
+ private int mFixInterval = 1000;
+
+ // true if we started navigation
+ private boolean mStarted;
+
+ // capabilities of the GPS engine
+ private int mEngineCapabilities;
+
+ // for calculating time to first fix
+ private long mFixRequestTime = 0;
+ // time to first fix for most recent session
+ private int mTTFF = 0;
+ // time we received our last fix
+ private long mLastFixTime;
+
+ private int mPositionMode;
+
+ // properties loaded from PROPERTIES_FILE
+ private Properties mProperties;
+ private String mNtpServer;
+ private String mSuplServerHost;
+ private int mSuplServerPort;
+ private String mC2KServerHost;
+ private int mC2KServerPort;
+
+ private final Context mContext;
+ private final ILocationManager mLocationManager;
+ private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
+ private Bundle mLocationExtras = new Bundle();
+ private ArrayList<Listener> mListeners = new ArrayList<Listener>();
+
+ // GpsLocationProvider's handler thread
+ private final Thread mThread;
+ // Handler for processing events in mThread.
+ private Handler mHandler;
+ // Used to signal when our main thread has initialized everything
+ private final CountDownLatch mInitializedLatch = new CountDownLatch(1);
+ // Thread for receiving events from the native code
+ private Thread mEventThread;
+
+ private String mAGpsApn;
+ private int mAGpsDataConnectionState;
+ private final ConnectivityManager mConnMgr;
+ private final GpsNetInitiatedHandler mNIHandler;
+
+ // Wakelocks
+ private final static String WAKELOCK_KEY = "GpsLocationProvider";
+ private final PowerManager.WakeLock mWakeLock;
+ // bitfield of pending messages to our Handler
+ // used only for messages that cannot have multiple instances queued
+ private int mPendingMessageBits;
+ // separate counter for ADD_LISTENER and REMOVE_LISTENER messages,
+ // which might have multiple instances queued
+ private int mPendingListenerMessages;
+
+ // Alarms
+ private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
+ private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
+ private final AlarmManager mAlarmManager;
+ private final PendingIntent mWakeupIntent;
+ private final PendingIntent mTimeoutIntent;
+
+ private final IBatteryStats mBatteryStats;
+ private final SparseIntArray mClientUids = new SparseIntArray();
+
+ // how often to request NTP time, in milliseconds
+ // current setting 4 hours
+ private static final long NTP_INTERVAL = 4*60*60*1000;
+ // how long to wait if we have a network error in NTP or XTRA downloading
+ // current setting - 5 minutes
+ private static final long RETRY_INTERVAL = 5*60*1000;
+
+ // to avoid injecting bad NTP time, we reject any time fixes that differ from system time
+ // by more than 5 minutes.
+ private static final long MAX_NTP_SYSTEM_TIME_OFFSET = 5*60*1000;
+
+ private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
+ public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
+ if (listener == null) {
+ throw new NullPointerException("listener is null in addGpsStatusListener");
+ }
+
+ synchronized(mListeners) {
+ IBinder binder = listener.asBinder();
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener test = mListeners.get(i);
+ if (binder.equals(test.mListener.asBinder())) {
+ // listener already added
+ return;
+ }
+ }
+
+ Listener l = new Listener(listener);
+ binder.linkToDeath(l, 0);
+ mListeners.add(l);
+ }
+ }
+
+ public void removeGpsStatusListener(IGpsStatusListener listener) {
+ if (listener == null) {
+ throw new NullPointerException("listener is null in addGpsStatusListener");
+ }
+
+ synchronized(mListeners) {
+ IBinder binder = listener.asBinder();
+ Listener l = null;
+ int size = mListeners.size();
+ for (int i = 0; i < size && l == null; i++) {
+ Listener test = mListeners.get(i);
+ if (binder.equals(test.mListener.asBinder())) {
+ l = test;
+ }
+ }
+
+ if (l != null) {
+ mListeners.remove(l);
+ binder.unlinkToDeath(l, 0);
+ }
+ }
+ }
+ };
+
+ public IGpsStatusProvider getGpsStatusProvider() {
+ return mGpsStatusProvider;
+ }
+
+ private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
+ @Override public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(ALARM_WAKEUP)) {
+ if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
+ startNavigating();
+ } else if (action.equals(ALARM_TIMEOUT)) {
+ if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
+ hibernate();
+ }
+ }
+ };
+
+ public static boolean isSupported() {
+ return native_is_supported();
+ }
+
+ public GpsLocationProvider(Context context, ILocationManager locationManager) {
+ mContext = context;
+ mLocationManager = locationManager;
+ mNIHandler = new GpsNetInitiatedHandler(context);
+
+ mLocation.setExtras(mLocationExtras);
+
+ // Create a wake lock
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+ mWakeLock.setReferenceCounted(false);
+
+ mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
+ mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
+
+ mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ // Battery statistics service to be notified when GPS turns on or off
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
+ mProperties = new Properties();
+ try {
+ File file = new File(PROPERTIES_FILE);
+ FileInputStream stream = new FileInputStream(file);
+ mProperties.load(stream);
+ stream.close();
+ mNtpServer = mProperties.getProperty("NTP_SERVER", null);
+
+ mSuplServerHost = mProperties.getProperty("SUPL_HOST");
+ String portString = mProperties.getProperty("SUPL_PORT");
+ if (mSuplServerHost != null && portString != null) {
+ try {
+ mSuplServerPort = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
+ }
+ }
+
+ mC2KServerHost = mProperties.getProperty("C2K_HOST");
+ portString = mProperties.getProperty("C2K_PORT");
+ if (mC2KServerHost != null && portString != null) {
+ try {
+ mC2KServerPort = Integer.parseInt(portString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse C2K_PORT: " + portString);
+ }
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
+ }
+
+ // wait until we are fully initialized before returning
+ mThread = new GpsLocationProviderThread();
+ mThread.start();
+ while (true) {
+ try {
+ mInitializedLatch.await();
+ break;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ private void initialize() {
+ // register our receiver on our thread rather than the main thread
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ALARM_WAKEUP);
+ intentFilter.addAction(ALARM_TIMEOUT);
+ mContext.registerReceiver(mBroadcastReciever, intentFilter);
+ }
+
+ /**
+ * Returns the name of this provider.
+ */
+ public String getName() {
+ return LocationManager.GPS_PROVIDER;
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * data network (e.g., the Internet), false otherwise.
+ */
+ public boolean requiresNetwork() {
+ return true;
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ sendMessage(UPDATE_NETWORK_STATE, state, info);
+ }
+
+ private void handleUpdateNetworkState(int state, NetworkInfo info) {
+ mNetworkAvailable = (state == LocationProvider.AVAILABLE);
+
+ if (DEBUG) {
+ Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
+ + " info: " + info);
+ }
+
+ if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
+ && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
+ String apnName = info.getExtraInfo();
+ if (mNetworkAvailable && apnName != null && apnName.length() > 0) {
+ mAGpsApn = apnName;
+ if (DEBUG) Log.d(TAG, "call native_agps_data_conn_open");
+ native_agps_data_conn_open(apnName);
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
+ } else {
+ if (DEBUG) Log.d(TAG, "call native_agps_data_conn_failed");
+ mAGpsApn = null;
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ native_agps_data_conn_failed();
+ }
+ }
+
+ if (mNetworkAvailable) {
+ if (mInjectNtpTimePending) {
+ sendMessage(INJECT_NTP_TIME, 0, null);
+ }
+ if (mDownloadXtraDataPending) {
+ sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
+ }
+ }
+ }
+
+ private void handleInjectNtpTime() {
+ if (!mNetworkAvailable) {
+ // try again when network is up
+ mInjectNtpTimePending = true;
+ return;
+ }
+ mInjectNtpTimePending = false;
+
+ SntpClient client = new SntpClient();
+ long delay;
+
+ if (client.requestTime(mNtpServer, 10000)) {
+ long time = client.getNtpTime();
+ long timeReference = client.getNtpTimeReference();
+ int certainty = (int)(client.getRoundTripTime()/2);
+ long now = System.currentTimeMillis();
+
+ Log.d(TAG, "NTP server returned: "
+ + time + " (" + new Date(time)
+ + ") reference: " + timeReference
+ + " certainty: " + certainty
+ + " system time offset: " + (time - now));
+
+ native_inject_time(time, timeReference, certainty);
+ delay = NTP_INTERVAL;
+ } else {
+ if (DEBUG) Log.d(TAG, "requestTime failed");
+ delay = RETRY_INTERVAL;
+ }
+
+ // send delayed message for next NTP injection
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.removeMessages(INJECT_NTP_TIME);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+ }
+
+ private void handleDownloadXtraData() {
+ if (!mDownloadXtraDataPending) {
+ // try again when network is up
+ mDownloadXtraDataPending = true;
+ return;
+ }
+ mDownloadXtraDataPending = false;
+
+
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
+ byte[] data = xtraDownloader.downloadXtraData();
+ if (data != null) {
+ if (DEBUG) {
+ Log.d(TAG, "calling native_inject_xtra_data");
+ }
+ native_inject_xtra_data(data, data.length);
+ } else {
+ // try again later
+ // since this is delayed and not urgent we do not hold a wake lock here
+ mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL);
+ }
+ }
+
+ /**
+ * This is called to inform us when another location provider returns a location.
+ * Someday we might use this for network location injection to aid the GPS
+ */
+ public void updateLocation(Location location) {
+ sendMessage(UPDATE_LOCATION, 0, location);
+ }
+
+ private void handleUpdateLocation(Location location) {
+ if (location.hasAccuracy()) {
+ native_inject_location(location.getLatitude(), location.getLongitude(),
+ location.getAccuracy());
+ }
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * satellite-based positioning system (e.g., GPS), false
+ * otherwise.
+ */
+ public boolean requiresSatellite() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider requires access to an appropriate
+ * cellular network (e.g., to make use of cell tower IDs), false
+ * otherwise.
+ */
+ public boolean requiresCell() {
+ return false;
+ }
+
+ /**
+ * Returns true if the use of this provider may result in a
+ * monetary charge to the user, false if use is free. It is up to
+ * each provider to give accurate information.
+ */
+ public boolean hasMonetaryCost() {
+ return false;
+ }
+
+ /**
+ * Returns true if the provider is able to provide altitude
+ * information, false otherwise. A provider that reports altitude
+ * under most circumstances but may occassionally not report it
+ * should return true.
+ */
+ public boolean supportsAltitude() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider is able to provide speed
+ * information, false otherwise. A provider that reports speed
+ * under most circumstances but may occassionally not report it
+ * should return true.
+ */
+ public boolean supportsSpeed() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider is able to provide bearing
+ * information, false otherwise. A provider that reports bearing
+ * under most circumstances but may occassionally not report it
+ * should return true.
+ */
+ public boolean supportsBearing() {
+ return true;
+ }
+
+ /**
+ * Returns the power requirement for this provider.
+ *
+ * @return the power requirement for this provider, as one of the
+ * constants Criteria.POWER_REQUIREMENT_*.
+ */
+ public int getPowerRequirement() {
+ return Criteria.POWER_HIGH;
+ }
+
+ /**
+ * Returns the horizontal accuracy of this provider
+ *
+ * @return the accuracy of location from this provider, as one
+ * of the constants Criteria.ACCURACY_*.
+ */
+ public int getAccuracy() {
+ return Criteria.ACCURACY_FINE;
+ }
+
+ /**
+ * Enables this provider. When enabled, calls to getStatus()
+ * must be handled. Hardware may be started up
+ * when the provider is enabled.
+ */
+ public void enable() {
+ synchronized (mHandler) {
+ sendMessage(ENABLE, 1, null);
+ }
+ }
+
+ private void handleEnable() {
+ if (DEBUG) Log.d(TAG, "handleEnable");
+ if (mEnabled) return;
+ mEnabled = native_init();
+
+ if (mEnabled) {
+ if (mSuplServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+ }
+ if (mC2KServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
+ }
+
+ // run event listener thread while we are enabled
+ mEventThread = new GpsEventThread();
+ mEventThread.start();
+ } else {
+ Log.w(TAG, "Failed to enable location provider");
+ }
+ }
+
+ /**
+ * Disables this provider. When disabled, calls to getStatus()
+ * need not be handled. Hardware may be shut
+ * down while the provider is disabled.
+ */
+ public void disable() {
+ synchronized (mHandler) {
+ sendMessage(ENABLE, 0, null);
+ }
+ }
+
+ private void handleDisable() {
+ if (DEBUG) Log.d(TAG, "handleDisable");
+ if (!mEnabled) return;
+
+ mEnabled = false;
+ stopNavigating();
+ native_disable();
+
+ // make sure our event thread exits
+ if (mEventThread != null) {
+ try {
+ mEventThread.join();
+ } catch (InterruptedException e) {
+ Log.w(TAG, "InterruptedException when joining mEventThread");
+ }
+ mEventThread = null;
+ }
+
+ // do this before releasing wakelock
+ native_cleanup();
+
+ // The GpsEventThread does not wait for the GPS to shutdown
+ // so we need to report the GPS_STATUS_ENGINE_OFF event here
+ if (mNavigating) {
+ reportStatus(GPS_STATUS_SESSION_END);
+ }
+ if (mEngineOn) {
+ reportStatus(GPS_STATUS_ENGINE_OFF);
+ }
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public int getStatus(Bundle extras) {
+ if (extras != null) {
+ extras.putInt("satellites", mSvCount);
+ }
+ return mStatus;
+ }
+
+ private void updateStatus(int status, int svCount) {
+ if (status != mStatus || svCount != mSvCount) {
+ mStatus = status;
+ mSvCount = svCount;
+ mLocationExtras.putInt("satellites", svCount);
+ mStatusUpdateTime = SystemClock.elapsedRealtime();
+ }
+ }
+
+ public long getStatusUpdateTime() {
+ return mStatusUpdateTime;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ synchronized (mHandler) {
+ sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null);
+ }
+ }
+
+ private void handleEnableLocationTracking(boolean enable) {
+ if (enable) {
+ mTTFF = 0;
+ mLastFixTime = 0;
+ startNavigating();
+ } else {
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ mAlarmManager.cancel(mWakeupIntent);
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
+ stopNavigating();
+ }
+ }
+
+ public void setMinTime(long minTime) {
+ if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
+
+ if (minTime >= 0) {
+ mFixInterval = (int)minTime;
+
+ if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
+ mFixInterval, 0, 0)) {
+ Log.e(TAG, "set_position_mode failed in setMinTime()");
+ }
+ }
+ }
+ }
+
+ public String getInternalState() {
+ return native_get_internal_state();
+ }
+
+ private final class Listener implements IBinder.DeathRecipient {
+ final IGpsStatusListener mListener;
+
+ int mSensors = 0;
+
+ Listener(IGpsStatusListener listener) {
+ mListener = listener;
+ }
+
+ public void binderDied() {
+ if (DEBUG) Log.d(TAG, "GPS status listener died");
+
+ synchronized(mListeners) {
+ mListeners.remove(this);
+ }
+ if (mListener != null) {
+ mListener.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+ }
+
+ public void addListener(int uid) {
+ synchronized (mWakeLock) {
+ mPendingListenerMessages++;
+ mWakeLock.acquire();
+ Message m = Message.obtain(mHandler, ADD_LISTENER);
+ m.arg1 = uid;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private void handleAddListener(int uid) {
+ synchronized(mListeners) {
+ if (mClientUids.indexOfKey(uid) >= 0) {
+ // Shouldn't be here -- already have this uid.
+ Log.w(TAG, "Duplicate add listener for uid " + uid);
+ return;
+ }
+ mClientUids.put(uid, 0);
+ if (mNavigating) {
+ try {
+ mBatteryStats.noteStartGps(uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in addListener");
+ }
+ }
+ }
+ }
+
+ public void removeListener(int uid) {
+ synchronized (mWakeLock) {
+ mPendingListenerMessages++;
+ mWakeLock.acquire();
+ Message m = Message.obtain(mHandler, REMOVE_LISTENER);
+ m.arg1 = uid;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private void handleRemoveListener(int uid) {
+ synchronized(mListeners) {
+ if (mClientUids.indexOfKey(uid) < 0) {
+ // Shouldn't be here -- don't have this uid.
+ Log.w(TAG, "Unneeded remove listener for uid " + uid);
+ return;
+ }
+ mClientUids.delete(uid);
+ if (mNavigating) {
+ try {
+ mBatteryStats.noteStopGps(uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in removeListener");
+ }
+ }
+ }
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+
+ long identity = Binder.clearCallingIdentity();
+ boolean result = false;
+
+ if ("delete_aiding_data".equals(command)) {
+ result = deleteAidingData(extras);
+ } else if ("force_time_injection".equals(command)) {
+ sendMessage(INJECT_NTP_TIME, 0, null);
+ result = true;
+ } else if ("force_xtra_injection".equals(command)) {
+ if (native_supports_xtra()) {
+ xtraDownloadRequest();
+ result = true;
+ }
+ } else {
+ Log.w(TAG, "sendExtraCommand: unknown command " + command);
+ }
+
+ Binder.restoreCallingIdentity(identity);
+ return result;
+ }
+
+ private boolean deleteAidingData(Bundle extras) {
+ int flags;
+
+ if (extras == null) {
+ flags = GPS_DELETE_ALL;
+ } else {
+ flags = 0;
+ if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
+ if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
+ if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
+ if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
+ if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
+ if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
+ if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
+ if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
+ if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
+ if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
+ if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
+ if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
+ if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
+ }
+
+ if (flags != 0) {
+ native_delete_aiding_data(flags);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void startNavigating() {
+ if (!mStarted) {
+ if (DEBUG) Log.d(TAG, "startNavigating");
+ mStarted = true;
+ if (hasCapability(GPS_CAPABILITY_MSB) &&
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
+ mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ } else {
+ mPositionMode = GPS_POSITION_MODE_STANDALONE;
+ }
+
+ int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
+ if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
+ interval, 0, 0)) {
+ mStarted = false;
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
+ }
+ if (!native_start()) {
+ mStarted = false;
+ Log.e(TAG, "native_start failed in startNavigating()");
+ return;
+ }
+
+ // reset SV count to zero
+ updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
+ mFixRequestTime = System.currentTimeMillis();
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
+ // and our fix interval is not short
+ if (mFixInterval >= NO_FIX_TIMEOUT) {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
+ }
+ }
+ }
+ }
+
+ private void stopNavigating() {
+ if (DEBUG) Log.d(TAG, "stopNavigating");
+ if (mStarted) {
+ mStarted = false;
+ native_stop();
+ mTTFF = 0;
+ mLastFixTime = 0;
+ mLocationFlags = LOCATION_INVALID;
+
+ // reset SV count to zero
+ updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
+ }
+ }
+
+ private void hibernate() {
+ // stop GPS until our next fix interval arrives
+ stopNavigating();
+ mAlarmManager.cancel(mTimeoutIntent);
+ mAlarmManager.cancel(mWakeupIntent);
+ long now = SystemClock.elapsedRealtime();
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + mFixInterval, mWakeupIntent);
+ }
+
+ private boolean hasCapability(int capability) {
+ return ((mEngineCapabilities & capability) != 0);
+ }
+
+ /**
+ * called from native code to update our position.
+ */
+ private void reportLocation(int flags, double latitude, double longitude, double altitude,
+ float speed, float bearing, float accuracy, long timestamp) {
+ if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
+ " timestamp: " + timestamp);
+
+ synchronized (mLocation) {
+ mLocationFlags = flags;
+ if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mLocation.setLatitude(latitude);
+ mLocation.setLongitude(longitude);
+ mLocation.setTime(timestamp);
+ }
+ if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
+ mLocation.setAltitude(altitude);
+ } else {
+ mLocation.removeAltitude();
+ }
+ if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
+ mLocation.setSpeed(speed);
+ } else {
+ mLocation.removeSpeed();
+ }
+ if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
+ mLocation.setBearing(bearing);
+ } else {
+ mLocation.removeBearing();
+ }
+ if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
+ mLocation.setAccuracy(accuracy);
+ } else {
+ mLocation.removeAccuracy();
+ }
+
+ try {
+ mLocationManager.reportLocation(mLocation, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
+ }
+
+ mLastFixTime = System.currentTimeMillis();
+ // report time to first fix
+ if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mTTFF = (int)(mLastFixTime - mFixRequestTime);
+ if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF);
+
+ // notify status listeners
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onFirstFix(mTTFF);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in stopNavigating");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+ }
+
+ if (mStarted && mStatus != LocationProvider.AVAILABLE) {
+ // we want to time out if we do not receive a fix
+ // within the time out and we are requesting infrequent fixes
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
+
+ // send an intent to notify that the GPS is receiving fixes.
+ Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
+ mContext.sendBroadcast(intent);
+ updateStatus(LocationProvider.AVAILABLE, mSvCount);
+ }
+
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval > 1000) {
+ if (DEBUG) Log.d(TAG, "got fix, hibernating");
+ hibernate();
+ }
+ }
+
+ /**
+ * called from native code to update our status
+ */
+ private void reportStatus(int status) {
+ if (VERBOSE) Log.v(TAG, "reportStatus status: " + status);
+
+ synchronized(mListeners) {
+ boolean wasNavigating = mNavigating;
+
+ switch (status) {
+ case GPS_STATUS_SESSION_BEGIN:
+ mNavigating = true;
+ mEngineOn = true;
+ break;
+ case GPS_STATUS_SESSION_END:
+ mNavigating = false;
+ break;
+ case GPS_STATUS_ENGINE_ON:
+ mEngineOn = true;
+ break;
+ case GPS_STATUS_ENGINE_OFF:
+ mEngineOn = false;
+ mNavigating = false;
+ break;
+ }
+
+ if (wasNavigating != mNavigating) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ if (mNavigating) {
+ listener.mListener.onGpsStarted();
+ } else {
+ listener.mListener.onGpsStopped();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportStatus");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+
+ try {
+ // update battery stats
+ for (int i=mClientUids.size() - 1; i >= 0; i--) {
+ int uid = mClientUids.keyAt(i);
+ if (mNavigating) {
+ mBatteryStats.noteStartGps(uid);
+ } else {
+ mBatteryStats.noteStopGps(uid);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportStatus");
+ }
+
+ // send an intent to notify that the GPS has been enabled or disabled.
+ Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
+ mContext.sendBroadcast(intent);
+ }
+ }
+ }
+
+ /**
+ * called from native code to update SV info
+ */
+ private void reportSvStatus() {
+
+ int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
+
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
+ mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
+ mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportSvInfo");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "SV count: " + svCount +
+ " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
+ " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
+ for (int i = 0; i < svCount; i++) {
+ Log.v(TAG, "sv: " + mSvs[i] +
+ " snr: " + (float)mSnrs[i]/10 +
+ " elev: " + mSvElevations[i] +
+ " azimuth: " + mSvAzimuths[i] +
+ ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " E") +
+ ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " A") +
+ ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
+ }
+ }
+
+ updateStatus(mStatus, svCount);
+
+ if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
+ System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
+ // send an intent to notify that the GPS is no longer receiving fixes.
+ Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
+ mContext.sendBroadcast(intent);
+ updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
+ }
+ }
+
+ /**
+ * called from native code to update AGPS status
+ */
+ private void reportAGpsStatus(int type, int status) {
+ switch (status) {
+ case GPS_REQUEST_AGPS_DATA_CONN:
+ int result = mConnMgr.startUsingNetworkFeature(
+ ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+ if (result == Phone.APN_ALREADY_ACTIVE) {
+ if (mAGpsApn != null) {
+ native_agps_data_conn_open(mAGpsApn);
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
+ } else {
+ Log.e(TAG, "mAGpsApn not set when receiving Phone.APN_ALREADY_ACTIVE");
+ native_agps_data_conn_failed();
+ }
+ } else if (result == Phone.APN_REQUEST_STARTED) {
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
+ } else {
+ native_agps_data_conn_failed();
+ }
+ break;
+ case GPS_RELEASE_AGPS_DATA_CONN:
+ if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
+ mConnMgr.stopUsingNetworkFeature(
+ ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+ native_agps_data_conn_closed();
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ }
+ break;
+ case GPS_AGPS_DATA_CONNECTED:
+ // Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
+ break;
+ case GPS_AGPS_DATA_CONN_DONE:
+ // Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
+ break;
+ case GPS_AGPS_DATA_CONN_FAILED:
+ // Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
+ break;
+ }
+ }
+
+ /**
+ * called from native code to report NMEA data received
+ */
+ private void reportNmea(int index, long timestamp) {
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ if (size > 0) {
+ // don't bother creating the String if we have no listeners
+ int length = native_read_nmea(index, mNmeaBuffer, mNmeaBuffer.length);
+ String nmea = new String(mNmeaBuffer, 0, length);
+
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onNmeaReceived(timestamp, nmea);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportNmea");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * called from native code to inform us what the GPS engine capabilities are
+ */
+ private void setEngineCapabilities(int capabilities) {
+ mEngineCapabilities = capabilities;
+ }
+
+ /**
+ * called from native code to request XTRA data
+ */
+ private void xtraDownloadRequest() {
+ if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
+ sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
+ }
+
+ //=============================================================
+ // NI Client support
+ //=============================================================
+ private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
+ // Sends a response for an NI reqeust to HAL.
+ public boolean sendNiResponse(int notificationId, int userResponse)
+ {
+ // TODO Add Permission check
+
+ StringBuilder extrasBuf = new StringBuilder();
+
+ if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
+ ", response: " + userResponse);
+
+ native_send_ni_response(notificationId, userResponse);
+
+ return true;
+ }
+ };
+
+ public INetInitiatedListener getNetInitiatedListener() {
+ return mNetInitiatedListener;
+ }
+
+ // Called by JNI function to report an NI request.
+ @SuppressWarnings("deprecation")
+ public void reportNiNotification(
+ int notificationId,
+ int niType,
+ int notifyFlags,
+ int timeout,
+ int defaultResponse,
+ String requestorId,
+ String text,
+ int requestorIdEncoding,
+ int textEncoding,
+ String extras // Encoded extra data
+ )
+ {
+ Log.i(TAG, "reportNiNotification: entered");
+ Log.i(TAG, "notificationId: " + notificationId +
+ ", niType: " + niType +
+ ", notifyFlags: " + notifyFlags +
+ ", timeout: " + timeout +
+ ", defaultResponse: " + defaultResponse);
+
+ Log.i(TAG, "requestorId: " + requestorId +
+ ", text: " + text +
+ ", requestorIdEncoding: " + requestorIdEncoding +
+ ", textEncoding: " + textEncoding);
+
+ GpsNiNotification notification = new GpsNiNotification();
+
+ notification.notificationId = notificationId;
+ notification.niType = niType;
+ notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
+ notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
+ notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
+ notification.timeout = timeout;
+ notification.defaultResponse = defaultResponse;
+ notification.requestorId = requestorId;
+ notification.text = text;
+ notification.requestorIdEncoding = requestorIdEncoding;
+ notification.textEncoding = textEncoding;
+
+ // Process extras, assuming the format is
+ // one of more lines of "key = value"
+ Bundle bundle = new Bundle();
+
+ if (extras == null) extras = "";
+ Properties extraProp = new Properties();
+
+ try {
+ extraProp.load(new StringBufferInputStream(extras));
+ }
+ catch (IOException e)
+ {
+ Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
+ }
+
+ for (Entry<Object, Object> ent : extraProp.entrySet())
+ {
+ bundle.putString((String) ent.getKey(), (String) ent.getValue());
+ }
+
+ notification.extras = bundle;
+
+ mNIHandler.handleNiNotification(notification);
+ }
+
+ // this thread is used to receive events from the native code.
+ // native_wait_for_event() will callback to us via reportLocation(), reportStatus(), etc.
+ // this is necessary because native code cannot call Java on a thread that the JVM does
+ // not know about.
+ private final class GpsEventThread extends Thread {
+
+ public GpsEventThread() {
+ super("GpsEventThread");
+ }
+
+ public void run() {
+ if (DEBUG) Log.d(TAG, "GpsEventThread starting");
+ // Exit as soon as disable() is called instead of waiting for the GPS to stop.
+ while (mEnabled) {
+ // this will wait for an event from the GPS,
+ // which will be reported via reportLocation or reportStatus
+ native_wait_for_event();
+ }
+ if (DEBUG) Log.d(TAG, "GpsEventThread exiting");
+ }
+ }
+
+ private void sendMessage(int message, int arg, Object obj) {
+ // hold a wake lock while messages are pending
+ synchronized (mWakeLock) {
+ mPendingMessageBits |= (1 << message);
+ mWakeLock.acquire();
+ mHandler.removeMessages(message);
+ Message m = Message.obtain(mHandler, message);
+ m.arg1 = arg;
+ m.obj = obj;
+ mHandler.sendMessage(m);
+ }
+ }
+
+ private final class ProviderHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg)
+ {
+ int message = msg.what;
+ switch (message) {
+ case ENABLE:
+ if (msg.arg1 == 1) {
+ handleEnable();
+ } else {
+ handleDisable();
+ }
+ break;
+ case ENABLE_TRACKING:
+ handleEnableLocationTracking(msg.arg1 == 1);
+ break;
+ case UPDATE_NETWORK_STATE:
+ handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
+ break;
+ case INJECT_NTP_TIME:
+ handleInjectNtpTime();
+ break;
+ case DOWNLOAD_XTRA_DATA:
+ if (native_supports_xtra()) {
+ handleDownloadXtraData();
+ }
+ break;
+ case UPDATE_LOCATION:
+ handleUpdateLocation((Location)msg.obj);
+ break;
+ case ADD_LISTENER:
+ handleAddListener(msg.arg1);
+ break;
+ case REMOVE_LISTENER:
+ handleRemoveListener(msg.arg1);
+ break;
+ }
+ // release wake lock if no messages are pending
+ synchronized (mWakeLock) {
+ mPendingMessageBits &= ~(1 << message);
+ if (message == ADD_LISTENER || message == REMOVE_LISTENER) {
+ mPendingListenerMessages--;
+ }
+ if (mPendingMessageBits == 0 && mPendingListenerMessages == 0) {
+ mWakeLock.release();
+ }
+ }
+ }
+ };
+
+ private final class GpsLocationProviderThread extends Thread {
+
+ public GpsLocationProviderThread() {
+ super("GpsLocationProvider");
+ }
+
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ initialize();
+ Looper.prepare();
+ mHandler = new ProviderHandler();
+ // signal when we are initialized and ready to go
+ mInitializedLatch.countDown();
+ Looper.loop();
+ }
+ }
+
+ // for GPS SV statistics
+ private static final int MAX_SVS = 32;
+ private static final int EPHEMERIS_MASK = 0;
+ private static final int ALMANAC_MASK = 1;
+ private static final int USED_FOR_FIX_MASK = 2;
+
+ // preallocated arrays, to avoid memory allocation in reportStatus()
+ private int mSvs[] = new int[MAX_SVS];
+ private float mSnrs[] = new float[MAX_SVS];
+ private float mSvElevations[] = new float[MAX_SVS];
+ private float mSvAzimuths[] = new float[MAX_SVS];
+ private int mSvMasks[] = new int[3];
+ private int mSvCount;
+ // preallocated to avoid memory allocation in reportNmea()
+ private byte[] mNmeaBuffer = new byte[120];
+
+ static { class_init_native(); }
+ private static native void class_init_native();
+ private static native boolean native_is_supported();
+
+ private native boolean native_init();
+ private native void native_disable();
+ private native void native_cleanup();
+ private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
+ int preferred_accuracy, int preferred_time);
+ private native boolean native_start();
+ private native boolean native_stop();
+ private native void native_delete_aiding_data(int flags);
+ private native void native_wait_for_event();
+ // returns number of SVs
+ // mask[0] is ephemeris mask and mask[1] is almanac mask
+ private native int native_read_sv_status(int[] svs, float[] snrs,
+ float[] elevations, float[] azimuths, int[] masks);
+ private native int native_read_nmea(int index, byte[] buffer, int bufferSize);
+ private native void native_inject_location(double latitude, double longitude, float accuracy);
+
+ // XTRA Support
+ private native void native_inject_time(long time, long timeReference, int uncertainty);
+ private native boolean native_supports_xtra();
+ private native void native_inject_xtra_data(byte[] data, int length);
+
+ // DEBUG Support
+ private native String native_get_internal_state();
+
+ // AGPS Support
+ private native void native_agps_data_conn_open(String apn);
+ private native void native_agps_data_conn_closed();
+ private native void native_agps_data_conn_failed();
+ private native void native_set_agps_server(int type, String hostname, int port);
+
+ // Network-initiated (NI) Support
+ private native void native_send_ni_response(int notificationId, int userResponse);
+}
diff --git a/services/java/com/android/server/location/GpsXtraDownloader.java b/services/java/com/android/server/location/GpsXtraDownloader.java
new file mode 100644
index 0000000..bc96980
--- /dev/null
+++ b/services/java/com/android/server/location/GpsXtraDownloader.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.net.Proxy;
+import android.net.http.AndroidHttpClient;
+import android.util.Config;
+import android.util.Log;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.params.ConnRouteParams;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Random;
+
+/**
+ * A class for downloading GPS XTRA data.
+ *
+ * {@hide}
+ */
+public class GpsXtraDownloader {
+
+ private static final String TAG = "GpsXtraDownloader";
+
+ private Context mContext;
+ private String[] mXtraServers;
+ // to load balance our server requests
+ private int mNextServerIndex;
+
+ GpsXtraDownloader(Context context, Properties properties) {
+ mContext = context;
+
+ // read XTRA servers from the Properties object
+ int count = 0;
+ String server1 = properties.getProperty("XTRA_SERVER_1");
+ String server2 = properties.getProperty("XTRA_SERVER_2");
+ String server3 = properties.getProperty("XTRA_SERVER_3");
+ if (server1 != null) count++;
+ if (server2 != null) count++;
+ if (server3 != null) count++;
+
+ if (count == 0) {
+ Log.e(TAG, "No XTRA servers were specified in the GPS configuration");
+ return;
+ } else {
+ mXtraServers = new String[count];
+ count = 0;
+ if (server1 != null) mXtraServers[count++] = server1;
+ if (server2 != null) mXtraServers[count++] = server2;
+ if (server3 != null) mXtraServers[count++] = server3;
+
+ // randomize first server
+ Random random = new Random();
+ mNextServerIndex = random.nextInt(count);
+ }
+ }
+
+ byte[] downloadXtraData() {
+ String proxyHost = Proxy.getHost(mContext);
+ int proxyPort = Proxy.getPort(mContext);
+ boolean useProxy = (proxyHost != null && proxyPort != -1);
+ byte[] result = null;
+ int startIndex = mNextServerIndex;
+
+ if (mXtraServers == null) {
+ return null;
+ }
+
+ // load balance our requests among the available servers
+ while (result == null) {
+ result = doDownload(mXtraServers[mNextServerIndex], useProxy, proxyHost, proxyPort);
+
+ // increment mNextServerIndex and wrap around if necessary
+ mNextServerIndex++;
+ if (mNextServerIndex == mXtraServers.length) {
+ mNextServerIndex = 0;
+ }
+ // break if we have tried all the servers
+ if (mNextServerIndex == startIndex) break;
+ }
+
+ return result;
+ }
+
+ protected static byte[] doDownload(String url, boolean isProxySet,
+ String proxyHost, int proxyPort) {
+ if (Config.LOGD) Log.d(TAG, "Downloading XTRA data from " + url);
+
+ AndroidHttpClient client = null;
+ try {
+ client = AndroidHttpClient.newInstance("Android");
+ HttpUriRequest req = new HttpGet(url);
+
+ if (isProxySet) {
+ HttpHost proxy = new HttpHost(proxyHost, proxyPort);
+ ConnRouteParams.setDefaultProxy(req.getParams(), proxy);
+ }
+
+ req.addHeader(
+ "Accept",
+ "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic");
+
+ req.addHeader(
+ "x-wap-profile",
+ "http://www.openmobilealliance.org/tech/profiles/UAPROF/ccppschema-20021212#");
+
+ HttpResponse response = client.execute(req);
+ StatusLine status = response.getStatusLine();
+ if (status.getStatusCode() != 200) { // HTTP 200 is success.
+ if (Config.LOGD) Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
+ return null;
+ }
+
+ HttpEntity entity = response.getEntity();
+ byte[] body = null;
+ if (entity != null) {
+ try {
+ if (entity.getContentLength() > 0) {
+ body = new byte[(int) entity.getContentLength()];
+ DataInputStream dis = new DataInputStream(entity.getContent());
+ try {
+ dis.readFully(body);
+ } finally {
+ try {
+ dis.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Unexpected IOException.", e);
+ }
+ }
+ }
+ } finally {
+ if (entity != null) {
+ entity.consumeContent();
+ }
+ }
+ }
+ return body;
+ } catch (Exception e) {
+ if (Config.LOGD) Log.d(TAG, "error " + e);
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
new file mode 100644
index 0000000..a472143
--- /dev/null
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.location.Location;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * {@hide}
+ */
+public interface LocationProviderInterface {
+ String getName();
+ boolean requiresNetwork();
+ boolean requiresSatellite();
+ boolean requiresCell();
+ boolean hasMonetaryCost();
+ boolean supportsAltitude();
+ boolean supportsSpeed();
+ boolean supportsBearing();
+ int getPowerRequirement();
+ int getAccuracy();
+ boolean isEnabled();
+ void enable();
+ void disable();
+ int getStatus(Bundle extras);
+ long getStatusUpdateTime();
+ void enableLocationTracking(boolean enable);
+ String getInternalState();
+ void setMinTime(long minTime);
+ void updateNetworkState(int state, NetworkInfo info);
+ void updateLocation(Location location);
+ boolean sendExtraCommand(String command, Bundle extras);
+ void addListener(int uid);
+ void removeListener(int uid);
+}
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
new file mode 100644
index 0000000..3e118f9
--- /dev/null
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.ILocationProvider;
+import android.location.Location;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.internal.location.DummyLocationProvider;
+
+/**
+ * A class for proxying location providers implemented as services.
+ *
+ * {@hide}
+ */
+public class LocationProviderProxy implements LocationProviderInterface {
+
+ private static final String TAG = "LocationProviderProxy";
+
+ private final Context mContext;
+ private final String mName;
+ private ILocationProvider mProvider;
+ private Handler mHandler;
+ private final Connection mServiceConnection = new Connection();
+
+ // cached values set by the location manager
+ private boolean mLocationTracking = false;
+ private boolean mEnabled = false;
+ private long mMinTime = -1;
+ private int mNetworkState;
+ private NetworkInfo mNetworkInfo;
+
+ // for caching requiresNetwork, requiresSatellite, etc.
+ private DummyLocationProvider mCachedAttributes;
+
+ // constructor for proxying location providers implemented in a separate service
+ public LocationProviderProxy(Context context, String name, String serviceName,
+ Handler handler) {
+ mContext = context;
+ mName = name;
+ mHandler = handler;
+ mContext.bindService(new Intent(serviceName), mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ private class Connection implements ServiceConnection {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "LocationProviderProxy.onServiceConnected " + className);
+ synchronized (this) {
+ mProvider = ILocationProvider.Stub.asInterface(service);
+ if (mProvider != null) {
+ mHandler.post(mServiceConnectedTask);
+ }
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "LocationProviderProxy.onServiceDisconnected " + className);
+ synchronized (this) {
+ mProvider = null;
+ }
+ }
+ }
+
+ private Runnable mServiceConnectedTask = new Runnable() {
+ public void run() {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ if (provider == null) {
+ return;
+ }
+ }
+
+ if (mCachedAttributes == null) {
+ try {
+ mCachedAttributes = new DummyLocationProvider(mName);
+ mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
+ mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
+ mCachedAttributes.setRequiresCell(provider.requiresCell());
+ mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
+ mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
+ mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
+ mCachedAttributes.setSupportsBearing(provider.supportsBearing());
+ mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
+ mCachedAttributes.setAccuracy(provider.getAccuracy());
+ } catch (RemoteException e) {
+ mCachedAttributes = null;
+ }
+ }
+
+ // resend previous values from the location manager if the service has restarted
+ try {
+ if (mEnabled) {
+ provider.enable();
+ }
+ if (mLocationTracking) {
+ provider.enableLocationTracking(true);
+ }
+ if (mMinTime >= 0) {
+ provider.setMinTime(mMinTime);
+ }
+ if (mNetworkInfo != null) {
+ provider.updateNetworkState(mNetworkState, mNetworkInfo);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ };
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean requiresNetwork() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.requiresNetwork();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean requiresSatellite() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.requiresSatellite();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean requiresCell() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.requiresCell();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean hasMonetaryCost() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.hasMonetaryCost();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean supportsAltitude() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.supportsAltitude();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean supportsSpeed() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.supportsSpeed();
+ } else {
+ return false;
+ }
+ }
+
+ public boolean supportsBearing() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.supportsBearing();
+ } else {
+ return false;
+ }
+ }
+
+ public int getPowerRequirement() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.getPowerRequirement();
+ } else {
+ return -1;
+ }
+ }
+
+ public int getAccuracy() {
+ if (mCachedAttributes != null) {
+ return mCachedAttributes.getAccuracy();
+ } else {
+ return -1;
+ }
+ }
+
+ public void enable() {
+ mEnabled = true;
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.enable();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void disable() {
+ mEnabled = false;
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.disable();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public int getStatus(Bundle extras) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.getStatus(extras);
+ } catch (RemoteException e) {
+ }
+ }
+ return 0;
+ }
+
+ public long getStatusUpdateTime() {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.getStatusUpdateTime();
+ } catch (RemoteException e) {
+ }
+ }
+ return 0;
+ }
+
+ public String getInternalState() {
+ try {
+ return mProvider.getInternalState();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getInternalState failed", e);
+ return null;
+ }
+ }
+
+ public boolean isLocationTracking() {
+ return mLocationTracking;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ mLocationTracking = enable;
+ if (!enable) {
+ mMinTime = -1;
+ }
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.enableLocationTracking(enable);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public long getMinTime() {
+ return mMinTime;
+ }
+
+ public void setMinTime(long minTime) {
+ mMinTime = minTime;
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.setMinTime(minTime);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ mNetworkState = state;
+ mNetworkInfo = info;
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.updateNetworkState(state, info);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void updateLocation(Location location) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.updateLocation(location);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.sendExtraCommand(command, extras);
+ } catch (RemoteException e) {
+ }
+ }
+ return false;
+ }
+
+ public void addListener(int uid) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.addListener(uid);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void removeListener(int uid) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ provider.removeListener(uid);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
new file mode 100644
index 0000000..e3f3346
--- /dev/null
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.location.ILocationManager;
+import android.location.Location;
+import android.location.LocationProvider;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.PrintWriterPrinter;
+
+import java.io.PrintWriter;
+
+/**
+ * A mock location provider used by LocationManagerService to implement test providers.
+ *
+ * {@hide}
+ */
+public class MockProvider implements LocationProviderInterface {
+ private final String mName;
+ private final ILocationManager mLocationManager;
+ private final boolean mRequiresNetwork;
+ private final boolean mRequiresSatellite;
+ private final boolean mRequiresCell;
+ private final boolean mHasMonetaryCost;
+ private final boolean mSupportsAltitude;
+ private final boolean mSupportsSpeed;
+ private final boolean mSupportsBearing;
+ private final int mPowerRequirement;
+ private final int mAccuracy;
+ private final Location mLocation;
+ private int mStatus;
+ private long mStatusUpdateTime;
+ private final Bundle mExtras = new Bundle();
+ private boolean mHasLocation;
+ private boolean mHasStatus;
+ private boolean mEnabled;
+
+ private static final String TAG = "MockProvider";
+
+ public MockProvider(String name, ILocationManager locationManager,
+ boolean requiresNetwork, boolean requiresSatellite,
+ boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
+ boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ mName = name;
+ mLocationManager = locationManager;
+ mRequiresNetwork = requiresNetwork;
+ mRequiresSatellite = requiresSatellite;
+ mRequiresCell = requiresCell;
+ mHasMonetaryCost = hasMonetaryCost;
+ mSupportsAltitude = supportsAltitude;
+ mSupportsBearing = supportsBearing;
+ mSupportsSpeed = supportsSpeed;
+ mPowerRequirement = powerRequirement;
+ mAccuracy = accuracy;
+ mLocation = new Location(name);
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void disable() {
+ mEnabled = false;
+ }
+
+ public void enable() {
+ mEnabled = true;
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public int getStatus(Bundle extras) {
+ if (mHasStatus) {
+ extras.clear();
+ extras.putAll(mExtras);
+ return mStatus;
+ } else {
+ return LocationProvider.AVAILABLE;
+ }
+ }
+
+ public long getStatusUpdateTime() {
+ return mStatusUpdateTime;
+ }
+
+ public int getAccuracy() {
+ return mAccuracy;
+ }
+
+ public int getPowerRequirement() {
+ return mPowerRequirement;
+ }
+
+ public boolean hasMonetaryCost() {
+ return mHasMonetaryCost;
+ }
+
+ public boolean requiresCell() {
+ return mRequiresCell;
+ }
+
+ public boolean requiresNetwork() {
+ return mRequiresNetwork;
+ }
+
+ public boolean requiresSatellite() {
+ return mRequiresSatellite;
+ }
+
+ public boolean supportsAltitude() {
+ return mSupportsAltitude;
+ }
+
+ public boolean supportsBearing() {
+ return mSupportsBearing;
+ }
+
+ public boolean supportsSpeed() {
+ return mSupportsSpeed;
+ }
+
+ public void setLocation(Location l) {
+ mLocation.set(l);
+ mHasLocation = true;
+ try {
+ mLocationManager.reportLocation(mLocation, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
+ }
+
+ public void clearLocation() {
+ mHasLocation = false;
+ }
+
+ public void setStatus(int status, Bundle extras, long updateTime) {
+ mStatus = status;
+ mStatusUpdateTime = updateTime;
+ mExtras.clear();
+ if (extras != null) {
+ mExtras.putAll(extras);
+ }
+ mHasStatus = true;
+ }
+
+ public void clearStatus() {
+ mHasStatus = false;
+ mStatusUpdateTime = 0;
+ }
+
+ public String getInternalState() {
+ return null;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ }
+
+ public void setMinTime(long minTime) {
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ }
+
+ public void updateLocation(Location location) {
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
+
+ public void addListener(int uid) {
+ }
+
+ public void removeListener(int uid) {
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + mName);
+ pw.println(prefix + "mHasLocation=" + mHasLocation);
+ pw.println(prefix + "mLocation:");
+ mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
+ pw.println(prefix + "mHasStatus=" + mHasStatus);
+ pw.println(prefix + "mStatus=" + mStatus);
+ pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
+ pw.println(prefix + "mExtras=" + mExtras);
+ }
+}
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
new file mode 100644
index 0000000..5ed1558
--- /dev/null
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.location.ILocationManager;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A passive location provider reports locations received from other providers
+ * for clients that want to listen passively without actually triggering
+ * location updates.
+ *
+ * {@hide}
+ */
+public class PassiveProvider implements LocationProviderInterface {
+
+ private static final String TAG = "PassiveProvider";
+
+ private final ILocationManager mLocationManager;
+ private boolean mTracking;
+
+ public PassiveProvider(ILocationManager locationManager) {
+ mLocationManager = locationManager;
+ }
+
+ public String getName() {
+ return LocationManager.PASSIVE_PROVIDER;
+ }
+
+ public boolean requiresNetwork() {
+ return false;
+ }
+
+ public boolean requiresSatellite() {
+ return false;
+ }
+
+ public boolean requiresCell() {
+ return false;
+ }
+
+ public boolean hasMonetaryCost() {
+ return false;
+ }
+
+ public boolean supportsAltitude() {
+ return false;
+ }
+
+ public boolean supportsSpeed() {
+ return false;
+ }
+
+ public boolean supportsBearing() {
+ return false;
+ }
+
+ public int getPowerRequirement() {
+ return -1;
+ }
+
+ public int getAccuracy() {
+ return -1;
+ }
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ public void enable() {
+ }
+
+ public void disable() {
+ }
+
+ public int getStatus(Bundle extras) {
+ if (mTracking) {
+ return LocationProvider.AVAILABLE;
+ } else {
+ return LocationProvider.TEMPORARILY_UNAVAILABLE;
+ }
+ }
+
+ public long getStatusUpdateTime() {
+ return -1;
+ }
+
+ public String getInternalState() {
+ return null;
+ }
+
+ public void enableLocationTracking(boolean enable) {
+ mTracking = enable;
+ }
+
+ public void setMinTime(long minTime) {
+ }
+
+ public void updateNetworkState(int state, NetworkInfo info) {
+ }
+
+ public void updateLocation(Location location) {
+ if (mTracking) {
+ try {
+ // pass the location back to the location manager
+ mLocationManager.reportLocation(location, true);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
+ }
+ }
+
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
+
+ public void addListener(int uid) {
+ }
+
+ public void removeListener(int uid) {
+ }
+}
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 3b0c436..35ccfe8 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -30,6 +30,7 @@ import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
@@ -62,7 +63,6 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.app.IBatteryStats;
-import com.android.internal.location.GpsLocationProvider;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.cdma.EriInfo;
@@ -390,8 +390,8 @@ public class StatusBarPolicy {
action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
updateWifi(intent);
}
- else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) ||
- action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION)) {
+ else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) ||
+ action.equals(LocationManager.GPS_FIX_CHANGE_ACTION)) {
updateGps(intent);
}
else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
@@ -536,8 +536,8 @@ public class StatusBarPolicy {
filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- filter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
- filter.addAction(GpsLocationProvider.GPS_FIX_CHANGE_ACTION);
+ filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+ filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
@@ -1181,7 +1181,7 @@ public class StatusBarPolicy {
final int ringerMode = audioManager.getRingerMode();
final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
ringerMode == AudioManager.RINGER_MODE_VIBRATE;
- final int iconId = (ringerMode == AudioManager.RINGER_MODE_VIBRATE)
+ final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)
? com.android.internal.R.drawable.stat_sys_ringer_vibrate
: com.android.internal.R.drawable.stat_sys_ringer_silent;
@@ -1292,13 +1292,13 @@ public class StatusBarPolicy {
private final void updateGps(Intent intent) {
final String action = intent.getAction();
- final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false);
+ final boolean enabled = intent.getBooleanExtra(LocationManager.EXTRA_GPS_ENABLED, false);
- if (action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION) && enabled) {
+ if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) {
// GPS is getting fixes
mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
mService.setIconVisibility(mGpsIcon, true);
- } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
+ } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
// GPS is off
mService.setIconVisibility(mGpsIcon, false);
} else {