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/AccessibilityManagerService.java71
-rw-r--r--services/java/com/android/server/AlarmManagerService.java11
-rw-r--r--services/java/com/android/server/AppWidgetService.java248
-rw-r--r--services/java/com/android/server/BackupManagerService.java232
-rw-r--r--services/java/com/android/server/BatteryService.java183
-rw-r--r--services/java/com/android/server/ClipboardService.java224
-rw-r--r--services/java/com/android/server/ConnectivityService.java1038
-rw-r--r--services/java/com/android/server/CountryDetectorService.java204
-rw-r--r--services/java/com/android/server/DemoDataSet.java140
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java1160
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java12
-rw-r--r--services/java/com/android/server/DockObserver.java10
-rw-r--r--services/java/com/android/server/DropBoxManagerService.java39
-rw-r--r--services/java/com/android/server/HeadsetObserver.java183
-rw-r--r--services/java/com/android/server/InputApplication.java13
-rw-r--r--services/java/com/android/server/InputApplicationHandle.java45
-rw-r--r--services/java/com/android/server/InputManager.java223
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java1264
-rw-r--r--services/java/com/android/server/InputWindow.java47
-rw-r--r--services/java/com/android/server/InputWindowHandle.java51
-rw-r--r--services/java/com/android/server/LightsService.java4
-rw-r--r--services/java/com/android/server/LocationManagerService.java7
-rw-r--r--services/java/com/android/server/MountService.java144
-rw-r--r--services/java/com/android/server/NetworkManagementService.java34
-rw-r--r--services/java/com/android/server/NetworkTimeUpdateService.java297
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java131
-rw-r--r--services/java/com/android/server/PackageManagerService.java257
-rw-r--r--services/java/com/android/server/PowerManagerService.java101
-rw-r--r--services/java/com/android/server/ProcessStats.java13
-rw-r--r--services/java/com/android/server/SamplingProfilerService.java117
-rw-r--r--services/java/com/android/server/ScreenRotationAnimation.java358
-rw-r--r--services/java/com/android/server/ShutdownActivity.java11
-rw-r--r--services/java/com/android/server/StatusBarManagerService.java142
-rw-r--r--services/java/com/android/server/StrictModeFlash.java114
-rw-r--r--services/java/com/android/server/SystemBackupAgent.java15
-rw-r--r--services/java/com/android/server/SystemServer.java168
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java246
-rw-r--r--services/java/com/android/server/ThrottleService.java234
-rw-r--r--services/java/com/android/server/UsbObserver.java207
-rw-r--r--services/java/com/android/server/UsbService.java252
-rwxr-xr-xservices/java/com/android/server/VibratorService.java5
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java2
-rw-r--r--services/java/com/android/server/WifiService.java2051
-rw-r--r--services/java/com/android/server/WifiStateTracker.java0
-rw-r--r--services/java/com/android/server/WifiWatchdogService.java34
-rw-r--r--services/java/com/android/server/WindowManagerService.java2093
-rw-r--r--services/java/com/android/server/WiredAccessoryObserver.java299
-rwxr-xr-xservices/java/com/android/server/am/ActivityManagerService.java826
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java72
-rw-r--r--services/java/com/android/server/am/ActivityStack.java553
-rw-r--r--services/java/com/android/server/am/AppWaitingForDebuggerDialog.java3
-rw-r--r--services/java/com/android/server/am/BaseErrorDialog.java2
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java39
-rw-r--r--services/java/com/android/server/am/FactoryErrorDialog.java4
-rw-r--r--services/java/com/android/server/am/LaunchWarningWindow.java8
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java39
-rw-r--r--services/java/com/android/server/am/TaskRecord.java14
-rw-r--r--services/java/com/android/server/am/UriPermissionOwner.java2
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java249
-rwxr-xr-xservices/java/com/android/server/location/ComprehensiveCountryDetector.java359
-rw-r--r--services/java/com/android/server/location/CountryDetectorBase.java72
-rwxr-xr-xservices/java/com/android/server/location/LocationBasedCountryDetector.java235
62 files changed, 10951 insertions, 4260 deletions
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
index 87de79a..04ae490 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -240,10 +240,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public void onChange(boolean selfChange) {
super.onChange(selfChange);
- mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
-
synchronized (mLock) {
+ mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
if (mIsEnabled) {
manageServicesLocked();
} else {
@@ -269,14 +268,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
});
}
- public void addClient(IAccessibilityManagerClient client) {
+ public boolean addClient(IAccessibilityManagerClient client) {
synchronized (mLock) {
- try {
- client.setEnabled(mIsEnabled);
- mClients.add(client);
- } catch (RemoteException re) {
- Slog.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re);
- }
+ mClients.add(client);
+ return mIsEnabled;
}
}
@@ -456,9 +451,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
} catch (RemoteException re) {
if (re instanceof DeadObjectException) {
Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
- synchronized (mLock) {
- removeDeadServiceLocked(service);
- }
+ removeDeadServiceLocked(service);
} else {
Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
}
@@ -472,19 +465,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* @return True if the service was removed, false otherwise.
*/
private boolean removeDeadServiceLocked(Service service) {
- mServices.remove(service);
- mHandler.removeMessages(service.mId);
-
if (Config.DEBUG) {
Slog.i(LOG_TAG, "Dead service " + service.mService + " removed");
}
-
- if (mServices.isEmpty()) {
- mIsEnabled = false;
- updateClientsLocked();
- }
-
- return true;
+ mHandler.removeMessages(service.mId);
+ return mServices.remove(service);
}
/**
@@ -547,11 +532,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
for (int i = 0, count = services.size(); i < count; i++) {
Service service = services.get(i);
-
- service.unbind();
- mComponentNameToServiceMap.remove(service.mComponentName);
+ if (service.unbind()) {
+ i--;
+ count--;
+ }
}
- services.clear();
}
/**
@@ -593,7 +578,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
Set<ComponentName> enabledServices) {
Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
- List<Service> services = mServices;
boolean isEnabled = mIsEnabled;
for (int i = 0, count = installedServices.size(); i < count; i++) {
@@ -602,15 +586,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
intalledService.name);
Service service = componentNameToServiceMap.get(componentName);
- if (isEnabled && enabledServices.contains(componentName)) {
- if (service == null) {
- new Service(componentName).bind();
+ if (isEnabled) {
+ if (enabledServices.contains(componentName)) {
+ if (service == null) {
+ service = new Service(componentName);
+ }
+ service.bind();
+ } else if (!enabledServices.contains(componentName)) {
+ if (service != null) {
+ service.unbind();
+ }
}
} else {
if (service != null) {
service.unbind();
- componentNameToServiceMap.remove(componentName);
- services.remove(service);
}
}
}
@@ -678,21 +667,31 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
/**
* Binds to the accessibility service.
+ *
+ * @return True if binding is successful.
*/
- public void bind() {
+ public boolean bind() {
if (mService == null) {
- mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
+ return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
}
+ return false;
}
/**
* Unbinds form the accessibility service and removes it from the data
* structures for service management.
+ *
+ * @return True if unbinding is successful.
*/
- public void unbind() {
+ public boolean unbind() {
if (mService != null) {
+ mService = null;
mContext.unbindService(this);
+ mComponentNameToServiceMap.remove(mComponentName);
+ mServices.remove(this);
+ return true;
}
+ return false;
}
/**
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 4e2f1e3..5a36417 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -124,6 +124,14 @@ class AlarmManagerService extends IAlarmManager.Stub {
public AlarmManagerService(Context context) {
mContext = context;
mDescriptor = init();
+
+ // We have to set current TimeZone info to kernel
+ // because kernel doesn't keep this after reboot
+ String tz = SystemProperties.get(TIMEZONE_PROPERTY);
+ if (tz != null) {
+ setTimeZone(tz);
+ }
+
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
@@ -653,7 +661,8 @@ class AlarmManagerService extends IAlarmManager.Stub {
remove(mTimeTickSender);
mClockReceiver.scheduleTimeTickEvent();
Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcast(intent);
}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 731fb22..59a540b 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -16,6 +16,23 @@
package com.android.server;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
@@ -24,46 +41,37 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.Intent.FilterComparison;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.Pair;
import android.util.Slog;
import android.util.TypedValue;
import android.util.Xml;
import android.widget.RemoteViews;
-import java.io.IOException;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.HashMap;
-import java.util.HashSet;
-
-import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.appwidget.IAppWidgetHost;
+import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.util.FastXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
+import com.android.internal.widget.IRemoteViewsAdapterConnection;
class AppWidgetService extends IAppWidgetService.Stub
{
@@ -107,6 +115,56 @@ class AppWidgetService extends IAppWidgetService.Stub
Host host;
}
+ /**
+ * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection.
+ * This needs to be a static inner class since a reference to the ServiceConnection is held
+ * globally and may lead us to leak AppWidgetService instances (if there were more than one).
+ */
+ static class ServiceConnectionProxy implements ServiceConnection {
+ private final AppWidgetService mAppWidgetService;
+ private final Pair<Integer, Intent.FilterComparison> mKey;
+ private final IBinder mConnectionCb;
+
+ ServiceConnectionProxy(AppWidgetService appWidgetService,
+ Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
+ mAppWidgetService = appWidgetService;
+ mKey = key;
+ mConnectionCb = connectionCb;
+ }
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IRemoteViewsAdapterConnection cb =
+ IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
+ try {
+ cb.onServiceConnected(service);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ public void onServiceDisconnected(ComponentName name) {
+ IRemoteViewsAdapterConnection cb =
+ IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb);
+ try {
+ cb.onServiceDisconnected();
+ mAppWidgetService.mServiceConnectionUpdateHandler.post(new Runnable() {
+ public void run() {
+ // We don't want to touch mBoundRemoteViewsServices from any other thread
+ // so queue this to run on the main thread.
+ if (mAppWidgetService.mBoundRemoteViewsServices.containsKey(mKey)) {
+ mAppWidgetService.mBoundRemoteViewsServices.remove(mKey);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ // Manages connections to RemoteViewsServices
+ private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
+ mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
+ private final Handler mServiceConnectionUpdateHandler = new Handler();
+
Context mContext;
Locale mLocale;
PackageManager mPackageManager;
@@ -144,6 +202,7 @@ class AppWidgetService extends IAppWidgetService.Stub
// update the provider list.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
mContext.registerReceiver(mBroadcastReceiver, filter);
@@ -293,6 +352,9 @@ class AppWidgetService extends IAppWidgetService.Stub
}
void deleteAppWidgetLocked(AppWidgetId id) {
+ // We first unbind all services that are bound to this id
+ unbindAppWidgetRemoteViewsServicesLocked(id);
+
Host host = id.host;
host.instances.remove(id);
pruneHostLocked(host);
@@ -375,6 +437,77 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
+ public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
+ synchronized (mAppWidgetIds) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+ final ComponentName componentName = intent.getComponent();
+ try {
+ final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
+ PackageManager.GET_PERMISSIONS);
+ if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
+ throw new SecurityException("Selected service does not require "
+ + android.Manifest.permission.BIND_REMOTEVIEWS
+ + ": " + componentName);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("Unknown component " + componentName);
+ }
+
+ // Bind to the RemoteViewsService (which will trigger a callback to the
+ // RemoteViewsAdapter)
+ Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
+ new FilterComparison(intent));
+ final ServiceConnection conn = new ServiceConnectionProxy(this, key, connection);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ mBoundRemoteViewsServices.put(key, conn);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
+ synchronized (mAppWidgetIds) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+
+ // Unbind from the RemoteViewsService (which will trigger a callback to the bound
+ // RemoteViewsAdapter)
+ Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
+ new FilterComparison(intent));
+ if (mBoundRemoteViewsServices.containsKey(key)) {
+ final ServiceConnection conn = mBoundRemoteViewsServices.get(key);
+ mBoundRemoteViewsServices.remove(key);
+ conn.onServiceDisconnected(null);
+ mContext.unbindService(conn);
+ }
+ }
+ }
+
+ private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
+ Iterator<Pair<Integer, Intent.FilterComparison>> it =
+ mBoundRemoteViewsServices.keySet().iterator();
+ int appWidgetId = id.appWidgetId;
+
+ // Unbind all connections to AppWidgets bound to this id
+ while (it.hasNext()) {
+ final Pair<Integer, Intent.FilterComparison> key = it.next();
+ if (key.first.intValue() == appWidgetId) {
+ final ServiceConnection conn = mBoundRemoteViewsServices.get(key);
+ it.remove();
+ conn.onServiceDisconnected(null);
+ mContext.unbindService(conn);
+ }
+ }
+ }
+
public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
synchronized (mAppWidgetIds) {
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
@@ -426,6 +559,40 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
+ public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ for (int i=0; i<N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ updateAppWidgetInstanceLocked(id, views, true);
+ }
+ }
+ }
+
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ for (int i=0; i<N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
+ }
+ }
+ }
+
public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
synchronized (mAppWidgetIds) {
Provider p = lookupProviderLocked(provider);
@@ -443,11 +610,17 @@ class AppWidgetService extends IAppWidgetService.Stub
}
void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
+ updateAppWidgetInstanceLocked(id, views, false);
+ }
+
+ void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
// allow for stale appWidgetIds and other badness
// lookup also checks that the calling process can access the appWidgetId
// drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
- id.views = views;
+
+ // We do not want to save this RemoteViews
+ if (!isPartialUpdate) id.views = views;
// is anyone listening?
if (id.host.callbacks != null) {
@@ -463,6 +636,25 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
+ void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
+ // allow for stale appWidgetIds and other badness
+ // lookup also checks that the calling process can access the appWidgetId
+ // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+ if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+ // is anyone listening?
+ if (id.host.callbacks != null) {
+ try {
+ // the lock is held, but this is a oneway call
+ id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
+ } catch (RemoteException e) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this instance.
+ id.host.callbacks = null;
+ }
+ }
+ }
+ }
+
public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
List<RemoteViews> updatedViews) {
int callingUid = enforceCallingUid(packageName);
@@ -584,6 +776,12 @@ class AppWidgetService extends IAppWidgetService.Stub
}
boolean addProviderLocked(ResolveInfo ri) {
+ if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ return false;
+ }
+ if (!ri.activityInfo.isEnabled()) {
+ return false;
+ }
Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
ri.activityInfo.name), ri);
if (p != null) {
@@ -742,6 +940,11 @@ class AppWidgetService extends IAppWidgetService.Stub
}
info.label = activityInfo.loadLabel(mPackageManager).toString();
info.icon = ri.getIconResource();
+ info.previewImage = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+ info.autoAdvanceViewId = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
+
sa.recycle();
} catch (Exception e) {
// Ok to catch Exception here, because anything going wrong because
@@ -1096,6 +1299,7 @@ class AppWidgetService extends IAppWidgetService.Stub
}
} else {
boolean added = false;
+ boolean changed = false;
String pkgList[] = null;
if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
@@ -1114,14 +1318,16 @@ class AppWidgetService extends IAppWidgetService.Stub
}
pkgList = new String[] { pkgName };
added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
}
if (pkgList == null || pkgList.length == 0) {
return;
}
- if (added) {
+ if (added || changed) {
synchronized (mAppWidgetIds) {
Bundle extras = intent.getExtras();
- if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+ if (changed || (extras != null &&
+ extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
for (String pkgName : pkgList) {
// The package was just upgraded
updateProvidersForPackageLocked(pkgName);
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index f1e226e..c48f360 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -107,6 +107,7 @@ class BackupManagerService extends IBackupManager.Stub {
private static final int MSG_RUN_INITIALIZE = 5;
private static final int MSG_RUN_GET_RESTORE_SETS = 6;
private static final int MSG_TIMEOUT = 7;
+ private static final int MSG_RESTORE_TIMEOUT = 8;
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -148,9 +149,9 @@ class BackupManagerService extends IBackupManager.Stub {
return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
}
}
- // Backups that we haven't started yet.
- HashMap<ApplicationInfo,BackupRequest> mPendingBackups
- = new HashMap<ApplicationInfo,BackupRequest>();
+ // Backups that we haven't started yet. Keys are package names.
+ HashMap<String,BackupRequest> mPendingBackups
+ = new HashMap<String,BackupRequest>();
// Pseudoname that we use for the Package Manager metadata "package"
static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
@@ -396,6 +397,21 @@ class BackupManagerService extends IBackupManager.Stub {
}
break;
}
+
+ case MSG_RESTORE_TIMEOUT:
+ {
+ synchronized (BackupManagerService.this) {
+ if (mActiveRestoreSession != null) {
+ // Client app left the restore session dangling. We know that it
+ // can't be in the middle of an actual restore operation because
+ // those are executed serially on this same handler thread. Clean
+ // up now.
+ Slog.w(TAG, "Restore session timed out; aborting");
+ post(mActiveRestoreSession.new EndRestoreRunnable(
+ BackupManagerService.this, mActiveRestoreSession));
+ }
+ }
+ }
}
}
}
@@ -590,6 +606,7 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
}
+ tf.close();
} catch (FileNotFoundException fnf) {
// Probably innocuous
Slog.v(TAG, "No ancestral data");
@@ -912,42 +929,48 @@ class BackupManagerService extends IBackupManager.Stub {
// 'packageName' is null, *all* participating apps will be removed.
void removePackageParticipantsLocked(String packageName) {
if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: " + packageName);
- List<PackageInfo> allApps = null;
+ List<String> allApps = new ArrayList<String>();
if (packageName != null) {
- allApps = new ArrayList<PackageInfo>();
- try {
- int flags = PackageManager.GET_SIGNATURES;
- allApps.add(mPackageManager.getPackageInfo(packageName, flags));
- } catch (Exception e) {
- // just skip it (???)
- }
+ allApps.add(packageName);
} else {
// all apps with agents
- allApps = allAgentPackages();
+ List<PackageInfo> knownPackages = allAgentPackages();
+ for (PackageInfo pkg : knownPackages) {
+ allApps.add(pkg.packageName);
+ }
}
removePackageParticipantsLockedInner(packageName, allApps);
}
private void removePackageParticipantsLockedInner(String packageName,
- List<PackageInfo> agents) {
+ List<String> allPackageNames) {
if (DEBUG) {
Slog.v(TAG, "removePackageParticipantsLockedInner (" + packageName
- + ") removing " + agents.size() + " entries");
- for (PackageInfo p : agents) {
+ + ") removing " + allPackageNames.size() + " entries");
+ for (String p : allPackageNames) {
Slog.v(TAG, " - " + p);
}
}
- for (PackageInfo pkg : agents) {
- if (packageName == null || pkg.packageName.equals(packageName)) {
- int uid = pkg.applicationInfo.uid;
+ for (String pkg : allPackageNames) {
+ if (packageName == null || pkg.equals(packageName)) {
+ int uid = -1;
+ try {
+ PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+ uid = info.applicationInfo.uid;
+ } catch (NameNotFoundException e) {
+ // we don't know this package name, so just skip it for now
+ continue;
+ }
+
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
if (set != null) {
// Find the existing entry with the same package name, and remove it.
// We can't just remove(app) because the instances are different.
for (ApplicationInfo entry: set) {
- if (entry.packageName.equals(pkg.packageName)) {
+ if (entry.packageName.equals(pkg)) {
+ if (DEBUG) Slog.v(TAG, " removing participant " + pkg);
set.remove(entry);
- removeEverBackedUp(pkg.packageName);
+ removeEverBackedUp(pkg);
break;
}
}
@@ -997,7 +1020,11 @@ class BackupManagerService extends IBackupManager.Stub {
// brute force but small code size
List<PackageInfo> allApps = allAgentPackages();
- removePackageParticipantsLockedInner(packageName, allApps);
+ List<String> allAppNames = new ArrayList<String>();
+ for (PackageInfo pkg : allApps) {
+ allAppNames.add(pkg.packageName);
+ }
+ removePackageParticipantsLockedInner(packageName, allAppNames);
addPackageParticipantsLockedInner(packageName, allApps);
}
@@ -1364,6 +1391,17 @@ class BackupManagerService extends IBackupManager.Stub {
for (BackupRequest request : mQueue) {
Slog.d(TAG, "starting agent for backup of " + request);
+ // Verify that the requested app exists; it might be something that
+ // requested a backup but was then uninstalled. The request was
+ // journalled and rather than tamper with the journal it's safer
+ // to sanity-check here.
+ try {
+ mPackageManager.getPackageInfo(request.appInfo.packageName, 0);
+ } catch (NameNotFoundException e) {
+ Slog.d(TAG, "Package does not exist; skipping");
+ continue;
+ }
+
IBackupAgent agent = null;
int mode = (request.fullBackup)
? IApplicationThread.BACKUP_MODE_FULL
@@ -1825,6 +1863,11 @@ class BackupManagerService extends IBackupManager.Stub {
} catch (RemoteException e) { /* can't happen */ }
}
+ // Furthermore we need to reset the session timeout clock
+ mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
+ mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
+ TIMEOUT_RESTORE_INTERVAL);
+
// done; we can finally release the wakelock
mWakelock.release();
}
@@ -2046,7 +2089,7 @@ class BackupManagerService extends IBackupManager.Stub {
// Add the caller to the set of pending backups. If there is
// one already there, then overwrite it, but no harm done.
BackupRequest req = new BackupRequest(app, false);
- if (mPendingBackups.put(app, req) == null) {
+ if (mPendingBackups.put(app.packageName, req) == null) {
// Journal this request in case of crash. The put()
// operation returned null when this package was not already
// in the set; we want to avoid touching the disk redundantly.
@@ -2343,6 +2386,55 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ // Supply the configuration Intent for the given transport. If the name is not one
+ // of the available transports, or if the transport does not supply any configuration
+ // UI, the method returns null.
+ public Intent getConfigurationIntent(String transportName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getConfigurationIntent");
+
+ synchronized (mTransports) {
+ final IBackupTransport transport = mTransports.get(transportName);
+ if (transport != null) {
+ try {
+ final Intent intent = transport.configurationIntent();
+ if (DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
+ + intent);
+ return intent;
+ } catch (RemoteException e) {
+ /* fall through to return null */
+ }
+ }
+ }
+
+ return null;
+ }
+
+ // Supply the configuration summary string for the given transport. If the name is
+ // not one of the available transports, or if the transport does not supply any
+ // summary / destination string, the method can return null.
+ //
+ // This string is used VERBATIM as the summary text of the relevant Settings item!
+ public String getDestinationString(String transportName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "getConfigurationIntent");
+
+ synchronized (mTransports) {
+ final IBackupTransport transport = mTransports.get(transportName);
+ if (transport != null) {
+ try {
+ final String text = transport.currentDestinationString();
+ if (DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
+ return text;
+ } catch (RemoteException e) {
+ /* fall through to return null */
+ }
+ }
+ }
+
+ return null;
+ }
+
// Callback: a requested backup agent has been instantiated. This should only
// be called from the Activity Manager.
public void agentConnected(String packageName, IBinder agentBinder) {
@@ -2456,10 +2548,23 @@ class BackupManagerService extends IBackupManager.Stub {
return null;
}
mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
+ mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
}
return mActiveRestoreSession;
}
+ void clearRestoreSession(ActiveRestoreSession currentSession) {
+ synchronized(this) {
+ if (currentSession != mActiveRestoreSession) {
+ Slog.e(TAG, "ending non-current restore session");
+ } else {
+ if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
+ mActiveRestoreSession = null;
+ mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
+ }
+ }
+ }
+
// Note that a currently-active backup agent has notified us that it has
// completed the given outstanding asynchronous backup/restore operation.
public void opComplete(int token) {
@@ -2478,6 +2583,7 @@ class BackupManagerService extends IBackupManager.Stub {
private String mPackageName;
private IBackupTransport mRestoreTransport = null;
RestoreSet[] mRestoreSets = null;
+ boolean mEnded = false;
ActiveRestoreSession(String packageName, String transport) {
mPackageName = packageName;
@@ -2492,6 +2598,10 @@ class BackupManagerService extends IBackupManager.Stub {
throw new IllegalArgumentException("Observer must not be null");
}
+ if (mEnded) {
+ throw new IllegalStateException("Restore session already ended");
+ }
+
long oldId = Binder.clearCallingIdentity();
try {
if (mRestoreTransport == null) {
@@ -2519,6 +2629,10 @@ class BackupManagerService extends IBackupManager.Stub {
if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
+ " observer=" + observer);
+ if (mEnded) {
+ throw new IllegalStateException("Restore session already ended");
+ }
+
if (mRestoreTransport == null || mRestoreSets == null) {
Slog.e(TAG, "Ignoring restoreAll() with no restore set");
return -1;
@@ -2550,6 +2664,10 @@ class BackupManagerService extends IBackupManager.Stub {
public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
+ if (mEnded) {
+ throw new IllegalStateException("Restore session already ended");
+ }
+
if (mPackageName != null) {
if (! mPackageName.equals(packageName)) {
Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
@@ -2606,34 +2724,59 @@ class BackupManagerService extends IBackupManager.Stub {
return 0;
}
- public synchronized void endRestoreSession() {
- if (DEBUG) Slog.d(TAG, "endRestoreSession");
+ // Posted to the handler to tear down a restore session in a cleanly synchronized way
+ class EndRestoreRunnable implements Runnable {
+ BackupManagerService mBackupManager;
+ ActiveRestoreSession mSession;
- synchronized (this) {
- long oldId = Binder.clearCallingIdentity();
- try {
- if (mRestoreTransport != null) mRestoreTransport.finishRestore();
- } catch (Exception e) {
- Slog.e(TAG, "Error in finishRestore", e);
- } finally {
- mRestoreTransport = null;
- Binder.restoreCallingIdentity(oldId);
- }
+ EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
+ mBackupManager = manager;
+ mSession = session;
}
- synchronized (BackupManagerService.this) {
- if (BackupManagerService.this.mActiveRestoreSession == this) {
- BackupManagerService.this.mActiveRestoreSession = null;
- } else {
- Slog.e(TAG, "ending non-current restore session");
+ public void run() {
+ // clean up the session's bookkeeping
+ synchronized (mSession) {
+ try {
+ if (mSession.mRestoreTransport != null) {
+ mSession.mRestoreTransport.finishRestore();
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in finishRestore", e);
+ } finally {
+ mSession.mRestoreTransport = null;
+ mSession.mEnded = true;
+ }
}
+
+ // clean up the BackupManagerService side of the bookkeeping
+ // and cancel any pending timeout message
+ mBackupManager.clearRestoreSession(mSession);
}
}
- }
+ public synchronized void endRestoreSession() {
+ if (DEBUG) Slog.d(TAG, "endRestoreSession");
+
+ if (mEnded) {
+ throw new IllegalStateException("Restore session already ended");
+ }
+
+ mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
+ }
+ }
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ long identityToken = Binder.clearCallingIdentity();
+ try {
+ dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void dumpInternal(PrintWriter pw) {
synchronized (mQueueLock) {
pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ " / " + (!mProvisioned ? "not " : "") + "provisioned / "
@@ -2647,12 +2790,15 @@ class BackupManagerService extends IBackupManager.Stub {
for (String t : listAllTransports()) {
pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
try {
- File dir = new File(mBaseStateDir, getTransport(t).transportDirName());
+ IBackupTransport transport = getTransport(t);
+ File dir = new File(mBaseStateDir, transport.transportDirName());
+ pw.println(" destination: " + transport.currentDestinationString());
+ pw.println(" intent: " + transport.configurationIntent());
for (File f : dir.listFiles()) {
pw.println(" " + f.getName() + " - " + f.length() + " state bytes");
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Error in transportDirName()", e);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in transport", e);
pw.println(" Error: " + e);
}
}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index fc4e06f..47599c8 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -43,6 +43,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
@@ -76,7 +77,7 @@ class BatteryService extends Binder {
// Used locally for determining when to make a last ditch effort to log
// discharge stats before the device dies.
- private static final int CRITICAL_BATTERY_LEVEL = 4;
+ private int mCriticalBatteryLevel;
private static final int DUMP_MAX_LENGTH = 24 * 1024;
private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-u" };
@@ -100,6 +101,7 @@ class BatteryService extends Binder {
private int mBatteryTemperature;
private String mBatteryTechnology;
private boolean mBatteryLevelCritical;
+ private int mInvalidCharger;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -108,6 +110,7 @@ class BatteryService extends Binder {
private int mLastBatteryVoltage;
private int mLastBatteryTemperature;
private boolean mLastBatteryLevelCritical;
+ private int mLastInvalidCharger;
private int mLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
@@ -118,18 +121,28 @@ class BatteryService extends Binder {
private long mDischargeStartTime;
private int mDischargeStartLevel;
+ private Led mLed;
+
private boolean mSentLowBatteryBroadcast = false;
- public BatteryService(Context context) {
+ public BatteryService(Context context, LightsService lights) {
mContext = context;
+ mLed = new Led(context, lights);
mBatteryStats = BatteryStatsService.getService();
+ mCriticalBatteryLevel = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_criticalBatteryWarningLevel);
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
- mUEventObserver.startObserving("SUBSYSTEM=power_supply");
+ mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");
+
+ // watch for invalid charger messages if the invalid_charger switch exists
+ if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
+ mInvalidChargerObserver.startObserving("DEVPATH=/devices/virtual/switch/invalid_charger");
+ }
// set initial status
update();
@@ -163,13 +176,24 @@ class BatteryService extends Binder {
return mPlugType;
}
- private UEventObserver mUEventObserver = new UEventObserver() {
+ private UEventObserver mPowerSupplyObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
update();
}
};
+ private UEventObserver mInvalidChargerObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
+ if (mInvalidCharger != invalidCharger) {
+ mInvalidCharger = invalidCharger;
+ update();
+ }
+ }
+ };
+
// returns battery level as a percentage
final int getBatteryLevel() {
return mBatteryLevel;
@@ -207,11 +231,14 @@ class BatteryService extends Binder {
private synchronized final void update() {
native_update();
+ processValues();
+ }
+ private void processValues() {
boolean logOutlier = false;
long dischargeDuration = 0;
- mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
+ mBatteryLevelCritical = mBatteryLevel <= mCriticalBatteryLevel;
if (mAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mUsbOnline) {
@@ -238,7 +265,8 @@ class BatteryService extends Binder {
mBatteryLevel != mLastBatteryLevel ||
mPlugType != mLastPlugType ||
mBatteryVoltage != mLastBatteryVoltage ||
- mBatteryTemperature != mLastBatteryTemperature) {
+ mBatteryTemperature != mLastBatteryTemperature ||
+ mInvalidCharger != mLastInvalidCharger) {
if (mPlugType != mLastPlugType) {
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -292,9 +320,9 @@ class BatteryService extends Binder {
* (becomes <= mLowBatteryWarningLevel).
*/
final boolean sendBatteryLow = !plugged
- && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
- && mBatteryLevel <= mLowBatteryWarningLevel
- && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
+ && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+ && mBatteryLevel <= mLowBatteryWarningLevel
+ && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
sendIntent();
@@ -322,6 +350,9 @@ class BatteryService extends Binder {
mContext.sendBroadcast(statusIntent);
}
+ // Update the battery LED
+ mLed.updateLightsLocked();
+
// This needs to be done after sendIntent() so that we get the lastest battery stats.
if (logOutlier && dischargeDuration != 0) {
logOutlier(dischargeDuration);
@@ -335,6 +366,7 @@ class BatteryService extends Binder {
mLastBatteryVoltage = mBatteryVoltage;
mLastBatteryTemperature = mBatteryTemperature;
mLastBatteryLevelCritical = mBatteryLevelCritical;
+ mLastInvalidCharger = mInvalidCharger;
}
}
@@ -356,16 +388,17 @@ class BatteryService extends Binder {
intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage);
intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature);
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
+ intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
- if (false) {
- Slog.d(TAG, "updateBattery level:" + mBatteryLevel +
+ if (true) {
+ Slog.d(TAG, "level:" + mBatteryLevel +
" scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
" health:" + mBatteryHealth + " present:" + mBatteryPresent +
" voltage: " + mBatteryVoltage +
" temperature: " + mBatteryTemperature +
" technology: " + mBatteryTechnology +
" AC powered:" + mAcOnline + " USB powered:" + mUsbOnline +
- " icon:" + icon );
+ " icon:" + icon + " invalid charger:" + mInvalidCharger);
}
ActivityManagerNative.broadcastStickyIntent(intent, null);
@@ -440,10 +473,15 @@ class BatteryService extends Binder {
private final int getIcon(int level) {
if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
return com.android.internal.R.drawable.stat_sys_battery_charge;
- } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING ||
- mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING ||
- mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
+ } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
return com.android.internal.R.drawable.stat_sys_battery;
+ } else if (mBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
+ || mBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
+ if (isPowered() && mBatteryLevel >= 100) {
+ return com.android.internal.R.drawable.stat_sys_battery_charge;
+ } else {
+ return com.android.internal.R.drawable.stat_sys_battery;
+ }
} else {
return com.android.internal.R.drawable.stat_sys_battery_unknown;
}
@@ -460,18 +498,109 @@ class BatteryService extends Binder {
return;
}
- synchronized (this) {
- pw.println("Current Battery Service state:");
- pw.println(" AC powered: " + mAcOnline);
- pw.println(" USB powered: " + mUsbOnline);
- pw.println(" status: " + mBatteryStatus);
- pw.println(" health: " + mBatteryHealth);
- pw.println(" present: " + mBatteryPresent);
- pw.println(" level: " + mBatteryLevel);
- pw.println(" scale: " + BATTERY_SCALE);
- pw.println(" voltage:" + mBatteryVoltage);
- pw.println(" temperature: " + mBatteryTemperature);
- pw.println(" technology: " + mBatteryTechnology);
+ if (args == null || args.length == 0) {
+ synchronized (this) {
+ pw.println("Current Battery Service state:");
+ pw.println(" AC powered: " + mAcOnline);
+ pw.println(" USB powered: " + mUsbOnline);
+ pw.println(" status: " + mBatteryStatus);
+ pw.println(" health: " + mBatteryHealth);
+ pw.println(" present: " + mBatteryPresent);
+ pw.println(" level: " + mBatteryLevel);
+ pw.println(" scale: " + BATTERY_SCALE);
+ pw.println(" voltage:" + mBatteryVoltage);
+ pw.println(" temperature: " + mBatteryTemperature);
+ pw.println(" technology: " + mBatteryTechnology);
+ }
+ } else if (false) {
+ // DO NOT SUBMIT WITH THIS TURNED ON
+ if (args.length == 3 && "set".equals(args[0])) {
+ String key = args[1];
+ String value = args[2];
+ try {
+ boolean update = true;
+ if ("ac".equals(key)) {
+ mAcOnline = Integer.parseInt(value) != 0;
+ } else if ("usb".equals(key)) {
+ mUsbOnline = Integer.parseInt(value) != 0;
+ } else if ("status".equals(key)) {
+ mBatteryStatus = Integer.parseInt(value);
+ } else if ("level".equals(key)) {
+ mBatteryLevel = Integer.parseInt(value);
+ } else if ("invalid".equals(key)) {
+ mInvalidCharger = Integer.parseInt(value);
+ } else {
+ update = false;
+ }
+ if (update) {
+ processValues();
+ }
+ } catch (NumberFormatException ex) {
+ pw.println("Bad value: " + value);
+ }
+ }
+ }
+ }
+
+ class Led {
+ private LightsService mLightsService;
+ private LightsService.Light mBatteryLight;
+
+ private int mBatteryLowARGB;
+ private int mBatteryMediumARGB;
+ private int mBatteryFullARGB;
+ private int mBatteryLedOn;
+ private int mBatteryLedOff;
+
+ private boolean mBatteryCharging;
+ private boolean mBatteryLow;
+ private boolean mBatteryFull;
+
+ Led(Context context, LightsService lights) {
+ mLightsService = lights;
+ mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
+
+ mBatteryLowARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLowARGB);
+ mBatteryMediumARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
+ mBatteryFullARGB = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+ mBatteryLedOn = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOn);
+ mBatteryLedOff = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLedOff);
+ }
+
+ /**
+ * Synchronize on BatteryService.
+ */
+ void updateLightsLocked() {
+ final int level = mBatteryLevel;
+ final int status = mBatteryStatus;
+ if (level < mLowBatteryWarningLevel) {
+ if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+ // Solid red when battery is charging
+ mBatteryLight.setColor(mBatteryLowARGB);
+ } else {
+ // Flash red when battery is low and not charging
+ mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
+ mBatteryLedOn, mBatteryLedOff);
+ }
+ } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
+ || status == BatteryManager.BATTERY_STATUS_FULL) {
+ if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
+ // Solid green when full or charging and nearly full
+ mBatteryLight.setColor(mBatteryFullARGB);
+ } else {
+ // Solid orange when charging and halfway full
+ mBatteryLight.setColor(mBatteryMediumARGB);
+ }
+ } else {
+ // No lights if not charging and not low
+ mBatteryLight.turnOff();
+ }
}
}
}
+
diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java
index aa8cded..062ab74 100644
--- a/services/java/com/android/server/ClipboardService.java
+++ b/services/java/com/android/server/ClipboardService.java
@@ -16,42 +16,240 @@
package com.android.server;
-import android.text.IClipboard;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.IClipboard;
+import android.content.IOnPrimaryClipChangedListener;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Pair;
+import android.util.Slog;
+
+import java.util.HashSet;
/**
* Implementation of the clipboard for copy and paste.
*/
public class ClipboardService extends IClipboard.Stub {
- private CharSequence mClipboard = "";
+ private final Context mContext;
+ private final IActivityManager mAm;
+ private final PackageManager mPm;
+ private final IBinder mPermissionOwner;
+
+ private final RemoteCallbackList<IOnPrimaryClipChangedListener> mPrimaryClipListeners
+ = new RemoteCallbackList<IOnPrimaryClipChangedListener>();
+
+ private ClipData mPrimaryClip;
+
+ private final HashSet<String> mActivePermissionOwners
+ = new HashSet<String>();
/**
* Instantiates the clipboard.
*/
- public ClipboardService(Context context) { }
+ public ClipboardService(Context context) {
+ mContext = context;
+ mAm = ActivityManagerNative.getDefault();
+ mPm = context.getPackageManager();
+ IBinder permOwner = null;
+ try {
+ permOwner = mAm.newUriPermissionOwner("clipboard");
+ } catch (RemoteException e) {
+ Slog.w("clipboard", "AM dead", e);
+ }
+ mPermissionOwner = permOwner;
+ }
+
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ Slog.w("clipboard", "Exception: ", e);
+ throw e;
+ }
+
+ }
- // javadoc from interface
- public void setClipboardText(CharSequence text) {
+ public void setPrimaryClip(ClipData clip) {
synchronized (this) {
- if (text == null) {
- text = "";
+ if (clip != null && clip.getItemCount() <= 0) {
+ throw new IllegalArgumentException("No items");
+ }
+ checkDataOwnerLocked(clip, Binder.getCallingUid());
+ clearActiveOwnersLocked();
+ mPrimaryClip = clip;
+ final int n = mPrimaryClipListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ try {
+ mPrimaryClipListeners.getBroadcastItem(i).dispatchPrimaryClipChanged();
+ } catch (RemoteException e) {
+
+ // The RemoteCallbackList will take care of removing
+ // the dead object for us.
+ }
}
+ mPrimaryClipListeners.finishBroadcast();
+ }
+ }
- mClipboard = text;
+ public ClipData getPrimaryClip(String pkg) {
+ synchronized (this) {
+ addActiveOwnerLocked(Binder.getCallingUid(), pkg);
+ return mPrimaryClip;
}
}
- // javadoc from interface
- public CharSequence getClipboardText() {
+ public ClipDescription getPrimaryClipDescription() {
synchronized (this) {
- return mClipboard;
+ return mPrimaryClip != null ? mPrimaryClip.getDescription() : null;
+ }
+ }
+
+ public boolean hasPrimaryClip() {
+ synchronized (this) {
+ return mPrimaryClip != null;
+ }
+ }
+
+ public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
+ synchronized (this) {
+ mPrimaryClipListeners.register(listener);
+ }
+ }
+
+ public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
+ synchronized (this) {
+ mPrimaryClipListeners.unregister(listener);
}
}
- // javadoc from interface
public boolean hasClipboardText() {
synchronized (this) {
- return mClipboard.length() > 0;
+ if (mPrimaryClip != null) {
+ CharSequence text = mPrimaryClip.getItemAt(0).getText();
+ return text != null && text.length() > 0;
+ }
+ return false;
+ }
+ }
+
+ private final void checkUriOwnerLocked(Uri uri, int uid) {
+ if (!"content".equals(uri.getScheme())) {
+ return;
+ }
+ long ident = Binder.clearCallingIdentity();
+ boolean allowed = false;
+ try {
+ // This will throw SecurityException for us.
+ mAm.checkGrantUriPermission(uid, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private final void checkItemOwnerLocked(ClipData.Item item, int uid) {
+ if (item.getUri() != null) {
+ checkUriOwnerLocked(item.getUri(), uid);
+ }
+ Intent intent = item.getIntent();
+ if (intent != null && intent.getData() != null) {
+ checkUriOwnerLocked(intent.getData(), uid);
+ }
+ }
+
+ private final void checkDataOwnerLocked(ClipData data, int uid) {
+ final int N = data.getItemCount();
+ for (int i=0; i<N; i++) {
+ checkItemOwnerLocked(data.getItemAt(i), uid);
+ }
+ }
+
+ private final void grantUriLocked(Uri uri, String pkg) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg, uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private final void grantItemLocked(ClipData.Item item, String pkg) {
+ if (item.getUri() != null) {
+ grantUriLocked(item.getUri(), pkg);
+ }
+ Intent intent = item.getIntent();
+ if (intent != null && intent.getData() != null) {
+ grantUriLocked(intent.getData(), pkg);
+ }
+ }
+
+ private final void addActiveOwnerLocked(int uid, String pkg) {
+ PackageInfo pi;
+ try {
+ pi = mPm.getPackageInfo(pkg, 0);
+ if (pi.applicationInfo.uid != uid) {
+ throw new SecurityException("Calling uid " + uid
+ + " does not own package " + pkg);
+ }
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException("Unknown package " + pkg, e);
+ }
+ if (mPrimaryClip != null && !mActivePermissionOwners.contains(pkg)) {
+ final int N = mPrimaryClip.getItemCount();
+ for (int i=0; i<N; i++) {
+ grantItemLocked(mPrimaryClip.getItemAt(i), pkg);
+ }
+ mActivePermissionOwners.add(pkg);
+ }
+ }
+
+ private final void revokeUriLocked(Uri uri) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private final void revokeItemLocked(ClipData.Item item) {
+ if (item.getUri() != null) {
+ revokeUriLocked(item.getUri());
+ }
+ Intent intent = item.getIntent();
+ if (intent != null && intent.getData() != null) {
+ revokeUriLocked(intent.getData());
+ }
+ }
+
+ private final void clearActiveOwnersLocked() {
+ mActivePermissionOwners.clear();
+ if (mPrimaryClip == null) {
+ return;
+ }
+ final int N = mPrimaryClip.getItemCount();
+ for (int i=0; i<N; i++) {
+ revokeItemLocked(mPrimaryClip.getItemAt(i));
}
}
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index bc102e4..bd3c554 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,23 +16,31 @@
package com.android.server;
-import android.app.Notification;
-import android.app.NotificationManager;
+import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.net.ConnectivityManager;
+import android.net.DummyDataStateTracker;
import android.net.IConnectivityManager;
+import android.net.LinkProperties;
import android.net.MobileDataStateTracker;
import android.net.NetworkInfo;
import android.net.NetworkStateTracker;
+import android.net.NetworkUtils;
+import android.net.Proxy;
+import android.net.ProxyProperties;
+import android.net.vpn.VpnManager;
import android.net.wifi.WifiStateTracker;
import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -42,12 +50,17 @@ import android.util.EventLog;
import android.util.Slog;
import com.android.internal.telephony.Phone;
-
import com.android.server.connectivity.Tethering;
import java.io.FileDescriptor;
+import java.io.FileWriter;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.GregorianCalendar;
import java.util.List;
@@ -56,7 +69,7 @@ import java.util.List;
*/
public class ConnectivityService extends IConnectivityManager.Stub {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final String TAG = "ConnectivityService";
// how long to wait before switching back to a radio's default network
@@ -65,7 +78,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
"android.telephony.apn-restore";
-
private Tethering mTethering;
private boolean mTetheringConfigValid = false;
@@ -82,6 +94,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private List mNetRequestersPids[];
+ private WifiWatchdogService mWifiWatchdogService;
+
// priority order of the nettrackers
// (excluding dynamically set mNetworkPreference)
// TODO - move mNetworkTypePreference into this
@@ -101,6 +115,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private boolean mTestMode;
private static ConnectivityService sServiceInstance;
+ private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true);
+
private static final int ENABLED = 1;
private static final int DISABLED = 0;
@@ -159,6 +175,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final int EVENT_SET_MOBILE_DATA =
MAX_NETWORK_STATE_TRACKER_EVENT + 7;
+ /**
+ * used internally to clear a wakelock when transitioning
+ * from one net to another
+ */
+ private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 8;
+
+ /**
+ * used internally to reload global proxy settings
+ */
+ private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 9;
+
private Handler mHandler;
// list of DeathRecipients used to make sure features are turned off when
@@ -168,10 +197,25 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private boolean mSystemReady;
private Intent mInitialBroadcast;
+ private PowerManager.WakeLock mNetTransitionWakeLock;
+ private String mNetTransitionWakeLockCausedBy = "";
+ private int mNetTransitionWakeLockSerialNumber;
+ private int mNetTransitionWakeLockTimeout;
+
+ private InetAddress mDefaultDns;
+
// used in DBG mode to track inet condition reports
private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
private ArrayList mInetLog;
+ // track the current default http proxy - tell the world if we get a new one (real change)
+ private ProxyProperties mDefaultProxy = null;
+ // track the global proxy.
+ private ProxyProperties mGlobalProxy = null;
+ private final Object mGlobalProxyLock = new Object();
+
+ private SettingsObserver mSettingsObserver;
+
private static class NetworkAttributes {
/**
* Class for holding settings read from resources.
@@ -207,64 +251,55 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
RadioAttributes[] mRadioAttributes;
- private static class ConnectivityThread extends Thread {
- private Context mContext;
-
- private ConnectivityThread(Context context) {
- super("ConnectivityThread");
- mContext = context;
- }
-
- @Override
- public void run() {
- Looper.prepare();
- synchronized (this) {
- sServiceInstance = new ConnectivityService(mContext);
- notifyAll();
- }
- Looper.loop();
- }
-
- public static ConnectivityService getServiceInstance(Context context) {
- ConnectivityThread thread = new ConnectivityThread(context);
- thread.start();
-
- synchronized (thread) {
- while (sServiceInstance == null) {
- try {
- // Wait until sServiceInstance has been initialized.
- thread.wait();
- } catch (InterruptedException ignore) {
- Slog.e(TAG,
- "Unexpected InterruptedException while waiting"+
- " for ConnectivityService thread");
- }
- }
- }
-
- return sServiceInstance;
+ public static synchronized ConnectivityService getInstance(Context context) {
+ if (sServiceInstance == null) {
+ sServiceInstance = new ConnectivityService(context);
}
- }
-
- public static ConnectivityService getInstance(Context context) {
- return ConnectivityThread.getServiceInstance(context);
+ return sServiceInstance;
}
private ConnectivityService(Context context) {
- if (DBG) Slog.v(TAG, "ConnectivityService starting up");
+ if (DBG) log("ConnectivityService starting up");
+
+ HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
+ handlerThread.start();
+ mHandler = new MyHandler(handlerThread.getLooper());
+
+ mBackgroundDataEnabled.set(Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.BACKGROUND_DATA, 1) == 1);
// setup our unique device name
- String id = Settings.Secure.getString(context.getContentResolver(),
- Settings.Secure.ANDROID_ID);
- if (id != null && id.length() > 0) {
- String name = new String("android_").concat(id);
- SystemProperties.set("net.hostname", name);
+ if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
+ String id = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.ANDROID_ID);
+ if (id != null && id.length() > 0) {
+ String name = new String("android_").concat(id);
+ SystemProperties.set("net.hostname", name);
+ }
+ }
+
+ // read our default dns server ip
+ String dns = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.DEFAULT_DNS_SERVER);
+ if (dns == null || dns.length() == 0) {
+ dns = context.getResources().getString(
+ com.android.internal.R.string.config_default_dns_server);
+ }
+ try {
+ mDefaultDns = InetAddress.getByName(dns);
+ } catch (UnknownHostException e) {
+ loge("Error setting defaultDns using " + dns);
}
mContext = context;
+
+ PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_networkTransitionTimeout);
+
mNetTrackers = new NetworkStateTracker[
ConnectivityManager.MAX_NETWORK_TYPE+1];
- mHandler = new MyHandler();
mNetworkPreference = getPersistedNetworkPreference();
@@ -277,11 +312,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
for (String raString : raStrings) {
RadioAttributes r = new RadioAttributes(raString);
if (r.mType > ConnectivityManager.MAX_RADIO_TYPE) {
- Slog.e(TAG, "Error in radioAttributes - ignoring attempt to define type " + r.mType);
+ loge("Error in radioAttributes - ignoring attempt to define type " + r.mType);
continue;
}
if (mRadioAttributes[r.mType] != null) {
- Slog.e(TAG, "Error in radioAttributes - ignoring attempt to redefine type " +
+ loge("Error in radioAttributes - ignoring attempt to redefine type " +
r.mType);
continue;
}
@@ -294,17 +329,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
try {
NetworkAttributes n = new NetworkAttributes(naString);
if (n.mType > ConnectivityManager.MAX_NETWORK_TYPE) {
- Slog.e(TAG, "Error in networkAttributes - ignoring attempt to define type " +
+ loge("Error in networkAttributes - ignoring attempt to define type " +
n.mType);
continue;
}
if (mNetAttributes[n.mType] != null) {
- Slog.e(TAG, "Error in networkAttributes - ignoring attempt to redefine type " +
+ loge("Error in networkAttributes - ignoring attempt to redefine type " +
n.mType);
continue;
}
if (mRadioAttributes[n.mRadio] == null) {
- Slog.e(TAG, "Error in networkAttributes - ignoring attempt to use undefined " +
+ loge("Error in networkAttributes - ignoring attempt to use undefined " +
"radio " + n.mRadio + " in network type " + n.mType);
continue;
}
@@ -356,30 +391,37 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* the number of different network types is not going
* to change very often.
*/
- boolean noMobileData = !getMobileDataEnabled();
for (int netType : mPriorityList) {
switch (mNetAttributes[netType].mRadio) {
case ConnectivityManager.TYPE_WIFI:
- if (DBG) Slog.v(TAG, "Starting Wifi Service.");
- WifiStateTracker wst = new WifiStateTracker(context, mHandler);
- WifiService wifiService = new WifiService(context, wst);
+ if (DBG) log("Starting Wifi Service.");
+ WifiStateTracker wst = new WifiStateTracker();
+ WifiService wifiService = new WifiService(context);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
- wifiService.startWifi();
+ wifiService.checkAndStartWifi();
mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
- wst.startMonitoring();
+ wst.startMonitoring(context, mHandler);
+
+ //TODO: as part of WWS refactor, create only when needed
+ mWifiWatchdogService = new WifiWatchdogService(context);
break;
case ConnectivityManager.TYPE_MOBILE:
- mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler,
- netType, mNetAttributes[netType].mName);
- mNetTrackers[netType].startMonitoring();
- if (noMobileData) {
- if (DBG) Slog.d(TAG, "tearing down Mobile networks due to setting");
- mNetTrackers[netType].teardown();
- }
+ mNetTrackers[netType] = new MobileDataStateTracker(netType,
+ mNetAttributes[netType].mName);
+ mNetTrackers[netType].startMonitoring(context, mHandler);
+ break;
+ case ConnectivityManager.TYPE_DUMMY:
+ mNetTrackers[netType] = new DummyDataStateTracker(netType,
+ mNetAttributes[netType].mName);
+ mNetTrackers[netType].startMonitoring(context, mHandler);
+ break;
+ case ConnectivityManager.TYPE_BLUETOOTH:
+ mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance();
+ mNetTrackers[netType].startMonitoring(context, mHandler);
break;
default:
- Slog.e(TAG, "Trying to create a DataStateTracker for an unknown radio type " +
+ loge("Trying to create a DataStateTracker for an unknown radio type " +
mNetAttributes[netType].mRadio);
continue;
}
@@ -389,12 +431,20 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
!mTethering.isDunRequired()) &&
(mTethering.getTetherableUsbRegexs().length != 0 ||
- mTethering.getTetherableWifiRegexs().length != 0) &&
+ mTethering.getTetherableWifiRegexs().length != 0 ||
+ mTethering.getTetherableBluetoothRegexs().length != 0) &&
mTethering.getUpstreamIfaceRegexs().length != 0);
if (DBG) {
mInetLog = new ArrayList();
}
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
+ mSettingsObserver.observe(mContext);
+
+ loadGlobalProxy();
+
+ VpnManager.startVpnService(context);
}
@@ -462,8 +512,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (t != mNetworkPreference && mNetTrackers[t] != null &&
mNetTrackers[t].getNetworkInfo().isConnected()) {
if (DBG) {
- Slog.d(TAG, "tearing down " +
- mNetTrackers[t].getNetworkInfo() +
+ log("tearing down " + mNetTrackers[t].getNetworkInfo() +
" in enforcePreference");
}
teardown(mNetTrackers[t]);
@@ -496,9 +545,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
NetworkStateTracker t = mNetTrackers[type];
NetworkInfo info = t.getNetworkInfo();
if (info.isConnected()) {
- if (DBG && type != mActiveDefaultNetwork) Slog.e(TAG,
- "connected default network is not " +
- "mActiveDefaultNetwork!");
+ if (DBG && type != mActiveDefaultNetwork) {
+ loge("connected default network is not mActiveDefaultNetwork!");
+ }
return info;
}
}
@@ -525,6 +574,38 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return result;
}
+ /**
+ * Return LinkProperties for the active (i.e., connected) default
+ * network interface. It is assumed that at most one default network
+ * is active at a time. If more than one is active, it is indeterminate
+ * which will be returned.
+ * @return the ip properties for the active network, or {@code null} if
+ * none is active
+ */
+ public LinkProperties getActiveLinkProperties() {
+ enforceAccessPermission();
+ for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
+ if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) {
+ continue;
+ }
+ NetworkStateTracker t = mNetTrackers[type];
+ NetworkInfo info = t.getNetworkInfo();
+ if (info.isConnected()) {
+ return t.getLinkProperties();
+ }
+ }
+ return null;
+ }
+
+ public LinkProperties getLinkProperties(int networkType) {
+ enforceAccessPermission();
+ if (ConnectivityManager.isNetworkTypeValid(networkType)) {
+ NetworkStateTracker t = mNetTrackers[networkType];
+ if (t != null) return t.getLinkProperties();
+ }
+ return null;
+ }
+
public boolean setRadios(boolean turnOn) {
boolean result = true;
enforceChangePermission();
@@ -579,14 +660,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
public void binderDied() {
- Slog.d(TAG, "ConnectivityService FeatureUser binderDied(" +
+ log("ConnectivityService FeatureUser binderDied(" +
mNetworkType + ", " + mFeature + ", " + mBinder + "), created " +
(System.currentTimeMillis() - mCreateTime) + " mSec ago");
stopUsingNetworkFeature(this, false);
}
public void expire() {
- Slog.d(TAG, "ConnectivityService FeatureUser expire(" +
+ log("ConnectivityService FeatureUser expire(" +
mNetworkType + ", " + mFeature + ", " + mBinder +"), created " +
(System.currentTimeMillis() - mCreateTime) + " mSec ago");
stopUsingNetworkFeature(this, false);
@@ -602,8 +683,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
public int startUsingNetworkFeature(int networkType, String feature,
IBinder binder) {
if (DBG) {
- Slog.d(TAG, "startUsingNetworkFeature for net " + networkType +
- ": " + feature);
+ log("startUsingNetworkFeature for net " + networkType + ": " + feature);
}
enforceChangePermission();
if (!ConnectivityManager.isNetworkTypeValid(networkType) ||
@@ -616,15 +696,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// TODO - move this into the MobileDataStateTracker
int usedNetworkType = networkType;
if(networkType == ConnectivityManager.TYPE_MOBILE) {
- if (!getMobileDataEnabled()) {
- if (DBG) Slog.d(TAG, "requested special network with data disabled - rejected");
- return Phone.APN_TYPE_NOT_AVAILABLE;
- }
if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
- } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) ||
+ TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -632,15 +709,18 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
NetworkStateTracker network = mNetTrackers[usedNetworkType];
if (network != null) {
+ Integer currentPid = new Integer(getCallingPid());
if (usedNetworkType != networkType) {
- Integer currentPid = new Integer(getCallingPid());
-
NetworkStateTracker radio = mNetTrackers[networkType];
NetworkInfo ni = network.getNetworkInfo();
if (ni.isAvailable() == false) {
- if (DBG) Slog.d(TAG, "special network not available");
- return Phone.APN_TYPE_NOT_AVAILABLE;
+ if (DBG) log("special network not available");
+ if (!TextUtils.equals(feature,Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
+ return Phone.APN_TYPE_NOT_AVAILABLE;
+ } else {
+ // else make the attempt anyway - probably giving REQUEST_STARTED below
+ }
}
synchronized(this) {
@@ -659,28 +739,29 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (ni.isConnected() == true) {
// add the pid-specific dns
handleDnsConfigurationChange(networkType);
- if (DBG) Slog.d(TAG, "special network already active");
+ if (DBG) log("special network already active");
return Phone.APN_ALREADY_ACTIVE;
}
- if (DBG) Slog.d(TAG, "special network already connecting");
+ if (DBG) log("special network already connecting");
return Phone.APN_REQUEST_STARTED;
}
// check if the radio in play can make another contact
// assume if cannot for now
- if (DBG) Slog.d(TAG, "reconnecting to special network");
+ if (DBG) log("reconnecting to special network");
network.reconnect();
return Phone.APN_REQUEST_STARTED;
} else {
+ // need to remember this unsupported request so we respond appropriately on stop
synchronized(this) {
mFeatureUsers.add(f);
+ if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
+ // this gets used for per-pid dns when connected
+ mNetRequestersPids[usedNetworkType].add(currentPid);
+ }
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
- f), getRestoreDefaultNetworkDelay());
-
- return network.startUsingNetworkFeature(feature,
- getCallingPid(), getCallingUid());
+ return -1;
}
}
return Phone.APN_TYPE_NOT_AVAILABLE;
@@ -712,7 +793,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return stopUsingNetworkFeature(u, true);
} else {
// none found!
- if (DBG) Slog.d(TAG, "ignoring stopUsingNetworkFeature - not a live request");
+ if (DBG) log("ignoring stopUsingNetworkFeature - not a live request");
return 1;
}
}
@@ -727,7 +808,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
boolean callTeardown = false; // used to carry our decision outside of sync block
if (DBG) {
- Slog.d(TAG, "stopUsingNetworkFeature for net " + networkType +
+ log("stopUsingNetworkFeature for net " + networkType +
": " + feature);
}
@@ -740,7 +821,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
synchronized(this) {
// check if this process still has an outstanding start request
if (!mFeatureUsers.contains(u)) {
- if (DBG) Slog.d(TAG, "ignoring - this process has no outstanding requests");
+ if (DBG) log("ignoring - this process has no outstanding requests");
return 1;
}
u.unlinkDeathRecipient();
@@ -758,7 +839,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (x.mUid == u.mUid && x.mPid == u.mPid &&
x.mNetworkType == u.mNetworkType &&
TextUtils.equals(x.mFeature, u.mFeature)) {
- if (DBG) Slog.d(TAG, "ignoring stopUsingNetworkFeature as dup is found");
+ if (DBG) log("ignoring stopUsingNetworkFeature as dup is found");
return 1;
}
}
@@ -771,7 +852,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
- } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) ||
+ TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -779,7 +861,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
tracker = mNetTrackers[usedNetworkType];
if (tracker == null) {
- if (DBG) Slog.d(TAG, "ignoring - no known tracker for net type " + usedNetworkType);
+ if (DBG) log("ignoring - no known tracker for net type " + usedNetworkType);
return -1;
}
if (usedNetworkType != networkType) {
@@ -787,24 +869,27 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mNetRequestersPids[usedNetworkType].remove(currentPid);
reassessPidDns(pid, true);
if (mNetRequestersPids[usedNetworkType].size() != 0) {
- if (DBG) Slog.d(TAG, "not tearing down special network - " +
+ if (DBG) log("not tearing down special network - " +
"others still using it");
return 1;
}
callTeardown = true;
+ } else {
+ if (DBG) log("not a known feature - dropping");
}
}
- if (DBG) Slog.d(TAG, "Doing network teardown");
+ if (DBG) log("Doing network teardown");
if (callTeardown) {
tracker.teardown();
return 1;
} else {
- // do it the old fashioned way
- return tracker.stopUsingNetworkFeature(feature, pid, uid);
+ return -1;
}
}
/**
+ * @deprecated use requestRouteToHostAddress instead
+ *
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
* @param networkType the type of the network over which traffic to the
@@ -814,6 +899,25 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* @return {@code true} on success, {@code false} on failure
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
+ InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
+
+ if (inetAddress == null) {
+ return false;
+ }
+
+ return requestRouteToHostAddress(networkType, inetAddress.getAddress());
+ }
+
+ /**
+ * Ensure that a network route exists to deliver traffic to the specified
+ * host via the specified network interface.
+ * @param networkType the type of the network over which traffic to the
+ * specified host is to be routed
+ * @param hostAddress the IP address of the host to which the route is
+ * desired
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
enforceChangePermission();
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
return false;
@@ -823,19 +927,51 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (tracker == null || !tracker.getNetworkInfo().isConnected() ||
tracker.isTeardownRequested()) {
if (DBG) {
- Slog.d(TAG, "requestRouteToHost on down network (" + networkType + ") - dropped");
+ log("requestRouteToHostAddress on down network " +
+ "(" + networkType + ") - dropped");
}
return false;
}
- return tracker.requestRouteToHost(hostAddress);
+ try {
+ InetAddress addr = InetAddress.getByAddress(hostAddress);
+ return addHostRoute(tracker, addr);
+ } catch (UnknownHostException e) {}
+ return false;
+ }
+
+ /**
+ * Ensure that a network route exists to deliver traffic to the specified
+ * host via the mobile data network.
+ * @param hostAddress the IP address of the host to which the route is desired,
+ * in network byte order.
+ * TODO - deprecate
+ * @return {@code true} on success, {@code false} on failure
+ */
+ private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress) {
+ if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) {
+ return false;
+ }
+
+ LinkProperties p = nt.getLinkProperties();
+ if (p == null) return false;
+ String interfaceName = p.getInterfaceName();
+
+ if (DBG) {
+ log("Requested host route to " + hostAddress + "(" + interfaceName + ")");
+ }
+ if (interfaceName != null) {
+ return NetworkUtils.addHostRoute(interfaceName, hostAddress, null);
+ } else {
+ if (DBG) loge("addHostRoute failed due to null interface name");
+ return false;
+ }
}
/**
* @see ConnectivityManager#getBackgroundDataSetting()
*/
public boolean getBackgroundDataSetting() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.BACKGROUND_DATA, 1) == 1;
+ return mBackgroundDataEnabled.get();
}
/**
@@ -846,6 +982,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
"ConnectivityService");
+ mBackgroundDataEnabled.set(allowBackgroundDataUsage);
+
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_BACKGROUND_DATA,
(allowBackgroundDataUsage ? ENABLED : DISABLED), 0));
}
@@ -864,10 +1002,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* @see ConnectivityManager#getMobileDataEnabled()
*/
public boolean getMobileDataEnabled() {
+ // TODO: This detail should probably be in DataConnectionTracker's
+ // which is where we store the value and maybe make this
+ // asynchronous.
enforceAccessPermission();
boolean retVal = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.MOBILE_DATA, 1) == 1;
- if (DBG) Slog.d(TAG, "getMobileDataEnabled returning " + retVal);
+ if (DBG) log("getMobileDataEnabled returning " + retVal);
return retVal;
}
@@ -876,47 +1017,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
public void setMobileDataEnabled(boolean enabled) {
enforceChangePermission();
- if (DBG) Slog.d(TAG, "setMobileDataEnabled(" + enabled + ")");
+ if (DBG) log("setMobileDataEnabled(" + enabled + ")");
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA,
(enabled ? ENABLED : DISABLED), 0));
}
private void handleSetMobileData(boolean enabled) {
- if (getMobileDataEnabled() == enabled) return;
-
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.MOBILE_DATA, enabled ? 1 : 0);
-
- if (enabled) {
- if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
- if (DBG) {
- Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
- }
- mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect();
- }
- } else {
- for (NetworkStateTracker nt : mNetTrackers) {
- if (nt == null) continue;
- int netType = nt.getNetworkInfo().getType();
- if (mNetAttributes[netType].mRadio == ConnectivityManager.TYPE_MOBILE) {
- if (DBG) Slog.d(TAG, "tearing down " + nt);
- nt.teardown();
- }
- }
- }
- }
-
- private int getNumConnectedNetworks() {
- int numConnectedNets = 0;
-
- for (NetworkStateTracker nt : mNetTrackers) {
- if (nt != null && nt.getNetworkInfo().isConnected() &&
- !nt.isTeardownRequested()) {
- ++numConnectedNets;
+ if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
+ if (DBG) {
+ Slog.d(TAG, mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
}
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE].setDataEnable(enabled);
}
- return numConnectedNets;
}
private void enforceAccessPermission() {
@@ -944,6 +1057,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
"ConnectivityService");
}
+ private void enforceConnectivityInternalPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONNECTIVITY_INTERNAL,
+ "ConnectivityService");
+ }
+
/**
* Handle a {@code DISCONNECTED} event. If this pertains to the non-active
* network, we ignore it. If it is for the active network, we send out a
@@ -987,18 +1106,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
info.getExtraInfo());
}
- NetworkStateTracker newNet = null;
if (mNetAttributes[prevNetType].isDefault()) {
- newNet = tryFailover(prevNetType);
- if (newNet != null) {
- NetworkInfo switchTo = newNet.getNetworkInfo();
- if (!switchTo.isConnected()) {
- // if the other net is connected they've already reset this and perhaps even gotten
- // a positive report we don't want to overwrite, but if not we need to clear this now
- // to turn our cellular sig strength white
- mDefaultInetConditionPublished = 0;
- intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
- }
+ tryFailover(prevNetType);
+ if (mActiveDefaultNetwork != -1) {
+ NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
} else {
mDefaultInetConditionPublished = 0; // we're not connected anymore
@@ -1014,86 +1125,45 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* If the failover network is already connected, then immediately send
* out a followup broadcast indicating successful failover
*/
- if (newNet != null && newNet.getNetworkInfo().isConnected()) {
- sendConnectedBroadcast(newNet.getNetworkInfo());
+ if (mActiveDefaultNetwork != -1) {
+ sendConnectedBroadcast(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo());
}
}
- // returns null if no failover available
- private NetworkStateTracker tryFailover(int prevNetType) {
+ private void tryFailover(int prevNetType) {
/*
- * If this is a default network, check if other defaults are available
- * or active
+ * If this is a default network, check if other defaults are available.
+ * Try to reconnect on all available and let them hash it out when
+ * more than one connects.
*/
- NetworkStateTracker newNet = null;
if (mNetAttributes[prevNetType].isDefault()) {
if (mActiveDefaultNetwork == prevNetType) {
mActiveDefaultNetwork = -1;
}
- int newType = -1;
- int newPriority = -1;
- boolean noMobileData = !getMobileDataEnabled();
+ // don't signal a reconnect for anything lower or equal priority than our
+ // current connected default
+ // TODO - don't filter by priority now - nice optimization but risky
+// int currentPriority = -1;
+// if (mActiveDefaultNetwork != -1) {
+// currentPriority = mNetAttributes[mActiveDefaultNetwork].mPriority;
+// }
for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
if (checkType == prevNetType) continue;
if (mNetAttributes[checkType] == null) continue;
- if (mNetAttributes[checkType].mRadio == ConnectivityManager.TYPE_MOBILE &&
- noMobileData) {
- Slog.e(TAG, "not failing over to mobile type " + checkType +
- " because Mobile Data Disabled");
- continue;
- }
- if (mNetAttributes[checkType].isDefault()) {
- /* TODO - if we have multiple nets we could use
- * we may want to put more thought into which we choose
- */
- if (checkType == mNetworkPreference) {
- newType = checkType;
- break;
- }
- if (mNetAttributes[checkType].mPriority > newPriority) {
- newType = checkType;
- newPriority = mNetAttributes[newType].mPriority;
- }
+ if (!mNetAttributes[checkType].isDefault()) continue;
+ if (!mNetTrackers[checkType].isAvailable()) continue;
+// if (currentPriority >= mNetAttributes[checkType].mPriority) continue;
+
+ NetworkStateTracker checkTracker = mNetTrackers[checkType];
+ NetworkInfo checkInfo = checkTracker.getNetworkInfo();
+ if (!checkInfo.isConnectedOrConnecting() || checkTracker.isTeardownRequested()) {
+ checkInfo.setFailover(true);
+ checkTracker.reconnect();
}
- }
-
- if (newType != -1) {
- newNet = mNetTrackers[newType];
- /**
- * See if the other network is available to fail over to.
- * If is not available, we enable it anyway, so that it
- * will be able to connect when it does become available,
- * but we report a total loss of connectivity rather than
- * report that we are attempting to fail over.
- */
- if (newNet.isAvailable()) {
- NetworkInfo switchTo = newNet.getNetworkInfo();
- switchTo.setFailover(true);
- if (!switchTo.isConnectedOrConnecting() ||
- newNet.isTeardownRequested()) {
- newNet.reconnect();
- }
- if (DBG) {
- if (switchTo.isConnected()) {
- Slog.v(TAG, "Switching to already connected " +
- switchTo.getTypeName());
- } else {
- Slog.v(TAG, "Attempting to switch to " +
- switchTo.getTypeName());
- }
- }
- } else {
- newNet.reconnect();
- newNet = null; // not officially avail.. try anyway, but
- // report no failover
- }
- } else {
- Slog.e(TAG, "Network failover failing.");
+ if (DBG) log("Attempting to switch to " + checkInfo.getTypeName());
}
}
-
- return newNet;
}
private void sendConnectedBroadcast(NetworkInfo info) {
@@ -1138,7 +1208,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} else {
reasonText = " (" + reason + ").";
}
- Slog.e(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
+ loge("Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
@@ -1156,17 +1226,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
info.setFailover(false);
}
- NetworkStateTracker newNet = null;
if (mNetAttributes[info.getType()].isDefault()) {
- newNet = tryFailover(info.getType());
- if (newNet != null) {
- NetworkInfo switchTo = newNet.getNetworkInfo();
- if (!switchTo.isConnected()) {
- // if the other net is connected they've already reset this and perhaps
- // even gotten a positive report we don't want to overwrite, but if not
- // we need to clear this now to turn our cellular sig strength white
- mDefaultInetConditionPublished = 0;
- }
+ tryFailover(info.getType());
+ if (mActiveDefaultNetwork != -1) {
+ NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
} else {
mDefaultInetConditionPublished = 0;
@@ -1180,8 +1243,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* If the failover network is already connected, then immediately send
* out a followup broadcast indicating successful failover
*/
- if (newNet != null && newNet.getNetworkInfo().isConnected()) {
- sendConnectedBroadcast(newNet.getNetworkInfo());
+ if (mActiveDefaultNetwork != -1) {
+ sendConnectedBroadcast(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo());
}
}
@@ -1203,6 +1266,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mInitialBroadcast = null;
}
}
+ // load the global proxy at startup
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
}
private void handleConnect(NetworkInfo info) {
@@ -1221,24 +1286,35 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mNetAttributes[type].mPriority) ||
mNetworkPreference == mActiveDefaultNetwork) {
// don't accept this one
- if (DBG) Slog.v(TAG, "Not broadcasting CONNECT_ACTION " +
+ if (DBG) {
+ log("Not broadcasting CONNECT_ACTION " +
"to torn down network " + info.getTypeName());
+ }
teardown(thisNet);
return;
} else {
// tear down the other
NetworkStateTracker otherNet =
mNetTrackers[mActiveDefaultNetwork];
- if (DBG) Slog.v(TAG, "Policy requires " +
- otherNet.getNetworkInfo().getTypeName() +
+ if (DBG) {
+ log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
" teardown");
+ }
if (!teardown(otherNet)) {
- Slog.e(TAG, "Network declined teardown request");
+ loge("Network declined teardown request");
return;
}
- if (isFailover) {
- otherNet.releaseWakeLock();
- }
+ }
+ }
+ synchronized (ConnectivityService.this) {
+ // have a new default network, release the transition wakelock in a second
+ // if it's held. The second pause is to allow apps to reconnect over the
+ // new network
+ if (mNetTransitionWakeLock.isHeld()) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+ mNetTransitionWakeLockSerialNumber, 0),
+ 1000);
}
}
mActiveDefaultNetwork = type;
@@ -1252,36 +1328,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
//reportNetworkCondition(mActiveDefaultNetwork, 100);
}
thisNet.setTeardownRequested(false);
- thisNet.updateNetworkSettings();
+ updateNetworkSettings(thisNet);
handleConnectivityChange(type);
sendConnectedBroadcast(info);
}
- private void handleScanResultsAvailable(NetworkInfo info) {
- int networkType = info.getType();
- if (networkType != ConnectivityManager.TYPE_WIFI) {
- if (DBG) Slog.v(TAG, "Got ScanResultsAvailable for " +
- info.getTypeName() + " network. Don't know how to handle.");
- }
-
- mNetTrackers[networkType].interpretScanResultsAvailable();
- }
-
- private void handleNotificationChange(boolean visible, int id,
- Notification notification) {
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (visible) {
- notificationManager.notify(id, notification);
- } else {
- notificationManager.cancel(id);
- }
- }
-
/**
- * After a change in the connectivity state of any network, We're mainly
- * concerned with making sure that the list of DNS servers is setupup
+ * After a change in the connectivity state of a network. We're mainly
+ * concerned with making sure that the list of DNS servers is set up
* according to which networks are connected, and ensuring that the
* right routing table entries exist.
*/
@@ -1294,19 +1348,159 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
if (mNetAttributes[netType].isDefault()) {
- mNetTrackers[netType].addDefaultRoute();
+ handleApplyDefaultProxy(netType);
+ addDefaultRoute(mNetTrackers[netType]);
} else {
- mNetTrackers[netType].addPrivateDnsRoutes();
+ addPrivateDnsRoutes(mNetTrackers[netType]);
}
} else {
if (mNetAttributes[netType].isDefault()) {
- mNetTrackers[netType].removeDefaultRoute();
+ removeDefaultRoute(mNetTrackers[netType]);
+ } else {
+ removePrivateDnsRoutes(mNetTrackers[netType]);
+ }
+ }
+ }
+
+ private void addPrivateDnsRoutes(NetworkStateTracker nt) {
+ boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
+ LinkProperties p = nt.getLinkProperties();
+ if (p == null) return;
+ String interfaceName = p.getInterfaceName();
+
+ if (DBG) {
+ log("addPrivateDnsRoutes for " + nt +
+ "(" + interfaceName + ") - mPrivateDnsRouteSet = " + privateDnsRouteSet);
+ }
+ if (interfaceName != null && !privateDnsRouteSet) {
+ Collection<InetAddress> dnsList = p.getDnses();
+ for (InetAddress dns : dnsList) {
+ if (DBG) log(" adding " + dns);
+ NetworkUtils.addHostRoute(interfaceName, dns, null);
+ }
+ nt.privateDnsRouteSet(true);
+ }
+ }
+
+ private void removePrivateDnsRoutes(NetworkStateTracker nt) {
+ // TODO - we should do this explicitly but the NetUtils api doesnt
+ // support this yet - must remove all. No worse than before
+ LinkProperties p = nt.getLinkProperties();
+ if (p == null) return;
+ String interfaceName = p.getInterfaceName();
+ boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
+ if (interfaceName != null && privateDnsRouteSet) {
+ if (DBG) {
+ log("removePrivateDnsRoutes for " + nt.getNetworkInfo().getTypeName() +
+ " (" + interfaceName + ")");
+ }
+ NetworkUtils.removeHostRoutes(interfaceName);
+ nt.privateDnsRouteSet(false);
+ }
+ }
+
+
+ private void addDefaultRoute(NetworkStateTracker nt) {
+ LinkProperties p = nt.getLinkProperties();
+ if (p == null) return;
+ String interfaceName = p.getInterfaceName();
+ InetAddress defaultGatewayAddr = p.getGateway();
+
+ if ((interfaceName != null) && (defaultGatewayAddr != null )) {
+ if (!NetworkUtils.addDefaultRoute(interfaceName, defaultGatewayAddr) && DBG) {
+ NetworkInfo networkInfo = nt.getNetworkInfo();
+ log("addDefaultRoute for " + networkInfo.getTypeName() +
+ " (" + interfaceName + "), GatewayAddr=" + defaultGatewayAddr);
+ }
+ }
+ }
+
+
+ public void removeDefaultRoute(NetworkStateTracker nt) {
+ LinkProperties p = nt.getLinkProperties();
+ if (p == null) return;
+ String interfaceName = p.getInterfaceName();
+
+ if (interfaceName != null) {
+ if ((NetworkUtils.removeDefaultRoute(interfaceName) >= 0) && DBG) {
+ NetworkInfo networkInfo = nt.getNetworkInfo();
+ log("removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
+ interfaceName + ")");
+ }
+ }
+ }
+
+ /**
+ * Reads the network specific TCP buffer sizes from SystemProperties
+ * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
+ * wide use
+ */
+ public void updateNetworkSettings(NetworkStateTracker nt) {
+ String key = nt.getTcpBufferSizesPropName();
+ String bufferSizes = SystemProperties.get(key);
+
+ if (bufferSizes.length() == 0) {
+ loge(key + " not found in system properties. Using defaults");
+
+ // Setting to default values so we won't be stuck to previous values
+ key = "net.tcp.buffersize.default";
+ bufferSizes = SystemProperties.get(key);
+ }
+
+ // Set values in kernel
+ if (bufferSizes.length() != 0) {
+ if (DBG) {
+ log("Setting TCP values: [" + bufferSizes
+ + "] which comes from [" + key + "]");
+ }
+ setBufferSize(bufferSizes);
+ }
+ }
+
+ /**
+ * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
+ * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
+ *
+ * @param bufferSizes in the format of "readMin, readInitial, readMax,
+ * writeMin, writeInitial, writeMax"
+ */
+ private void setBufferSize(String bufferSizes) {
+ try {
+ String[] values = bufferSizes.split(",");
+
+ if (values.length == 6) {
+ final String prefix = "/sys/kernel/ipv4/tcp_";
+ stringToFile(prefix + "rmem_min", values[0]);
+ stringToFile(prefix + "rmem_def", values[1]);
+ stringToFile(prefix + "rmem_max", values[2]);
+ stringToFile(prefix + "wmem_min", values[3]);
+ stringToFile(prefix + "wmem_def", values[4]);
+ stringToFile(prefix + "wmem_max", values[5]);
} else {
- mNetTrackers[netType].removePrivateDnsRoutes();
+ loge("Invalid buffersize string: " + bufferSizes);
}
+ } catch (IOException e) {
+ loge("Can't set tcp buffer sizes:" + e);
}
}
+ /**
+ * Writes string to file. Basically same as "echo -n $string > $filename"
+ *
+ * @param filename
+ * @param string
+ * @throws IOException
+ */
+ private void stringToFile(String filename, String string) throws IOException {
+ FileWriter out = new FileWriter(filename);
+ try {
+ out.write(string);
+ } finally {
+ out.close();
+ }
+ }
+
+
/**
* Adjust the per-process dns entries (net.dns<x>.<pid>) based
* on the highest priority active net which this process requested.
@@ -1314,7 +1508,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private void reassessPidDns(int myPid, boolean doBump)
{
- if (DBG) Slog.d(TAG, "reassessPidDns for pid " + myPid);
+ if (DBG) log("reassessPidDns for pid " + myPid);
for(int i : mPriorityList) {
if (mNetAttributes[i].isDefault()) {
continue;
@@ -1322,12 +1516,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
NetworkStateTracker nt = mNetTrackers[i];
if (nt.getNetworkInfo().isConnected() &&
!nt.isTeardownRequested()) {
+ LinkProperties p = nt.getLinkProperties();
+ if (p == null) continue;
List pids = mNetRequestersPids[i];
for (int j=0; j<pids.size(); j++) {
Integer pid = (Integer)pids.get(j);
if (pid.intValue() == myPid) {
- String[] dnsList = nt.getNameServers();
- writePidDns(dnsList, myPid);
+ Collection<InetAddress> dnses = p.getDnses();
+ writePidDns(dnses, myPid);
if (doBump) {
bumpDns();
}
@@ -1349,13 +1545,18 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- private void writePidDns(String[] dnsList, int pid) {
+ // return true if results in a change
+ private boolean writePidDns(Collection <InetAddress> dnses, int pid) {
int j = 1;
- for (String dns : dnsList) {
- if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
- SystemProperties.set("net.dns" + j++ + "." + pid, dns);
+ boolean changed = false;
+ for (InetAddress dns : dnses) {
+ String dnsString = dns.getHostAddress();
+ if (changed || !dnsString.equals(SystemProperties.get("net.dns" + j + "." + pid))) {
+ changed = true;
+ SystemProperties.set("net.dns" + j++ + "." + pid, dns.getHostAddress());
}
}
+ return changed;
}
private void bumpDns() {
@@ -1371,27 +1572,59 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} catch (NumberFormatException e) {}
}
SystemProperties.set("net.dnschange", "" + (n+1));
+ /*
+ * Tell the VMs to toss their DNS caches
+ */
+ Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ /*
+ * Connectivity events can happen before boot has completed ...
+ */
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent);
}
private void handleDnsConfigurationChange(int netType) {
// add default net's dns entries
NetworkStateTracker nt = mNetTrackers[netType];
if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
- String[] dnsList = nt.getNameServers();
+ LinkProperties p = nt.getLinkProperties();
+ if (p == null) return;
+ Collection<InetAddress> dnses = p.getDnses();
+ boolean changed = false;
if (mNetAttributes[netType].isDefault()) {
int j = 1;
- for (String dns : dnsList) {
- if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+ if (dnses.size() == 0 && mDefaultDns != null) {
+ String dnsString = mDefaultDns.getHostAddress();
+ if (!dnsString.equals(SystemProperties.get("net.dns1"))) {
if (DBG) {
- Slog.d(TAG, "adding dns " + dns + " for " +
+ log("no dns provided - using " + dnsString);
+ }
+ changed = true;
+ SystemProperties.set("net.dns1", dnsString);
+ }
+ j++;
+ } else {
+ for (InetAddress dns : dnses) {
+ String dnsString = dns.getHostAddress();
+ if (!changed && dnsString.equals(SystemProperties.get("net.dns" + j))) {
+ j++;
+ continue;
+ }
+ if (DBG) {
+ log("adding dns " + dns + " for " +
nt.getNetworkInfo().getTypeName());
}
- SystemProperties.set("net.dns" + j++, dns);
+ changed = true;
+ SystemProperties.set("net.dns" + j++, dnsString);
}
}
for (int k=j ; k<mNumDnsEntries; k++) {
- if (DBG) Slog.d(TAG, "erasing net.dns" + k);
- SystemProperties.set("net.dns" + k, "");
+ if (changed || !TextUtils.isEmpty(SystemProperties.get("net.dns" + k))) {
+ if (DBG) log("erasing net.dns" + k);
+ changed = true;
+ SystemProperties.set("net.dns" + k, "");
+ }
}
mNumDnsEntries = j;
} else {
@@ -1399,11 +1632,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
List pids = mNetRequestersPids[netType];
for (int y=0; y< pids.size(); y++) {
Integer pid = (Integer)pids.get(y);
- writePidDns(dnsList, pid.intValue());
+ changed = writePidDns(dnses, pid.intValue());
}
}
+ if (changed) bumpDns();
}
- bumpDns();
}
private int getRestoreDefaultNetworkDelay() {
@@ -1458,6 +1691,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
pw.println();
+ synchronized (this) {
+ pw.println("NetworkTranstionWakeLock is currently " +
+ (mNetTransitionWakeLock.isHeld() ? "" : "not ") + "held.");
+ pw.println("It was last requested for "+mNetTransitionWakeLockCausedBy);
+ }
+ pw.println();
+
mTethering.dump(fd, pw, args);
if (mInetLog != null) {
@@ -1471,6 +1711,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// must be stateless - things change under us.
private class MyHandler extends Handler {
+ public MyHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
NetworkInfo info;
@@ -1489,7 +1733,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (DBG) {
// TODO - remove this after we validate the dropping doesn't break
// anything
- Slog.d(TAG, "Dropping ConnectivityChange for " +
+ log("Dropping ConnectivityChange for " +
info.getTypeName() + ": " +
state + "/" + info.getDetailedState());
}
@@ -1497,7 +1741,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
mNetAttributes[type].mLastState = state;
- if (DBG) Slog.d(TAG, "ConnectivityChange for " +
+ if (DBG) log("ConnectivityChange for " +
info.getTypeName() + ": " +
state + "/" + info.getDetailedState());
@@ -1532,29 +1776,23 @@ public class ConnectivityService extends IConnectivityManager.Stub {
handleConnect(info);
}
break;
-
- case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
- info = (NetworkInfo) msg.obj;
- handleScanResultsAvailable(info);
- break;
-
- case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
- handleNotificationChange(msg.arg1 == 1, msg.arg2,
- (Notification) msg.obj);
- break;
-
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
info = (NetworkInfo) msg.obj;
type = info.getType();
- handleDnsConfigurationChange(type);
+ handleConnectivityChange(type);
break;
-
- case NetworkStateTracker.EVENT_ROAMING_CHANGED:
- // fill me in
- break;
-
- case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
- // fill me in
+ case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
+ String causedBy = null;
+ synchronized (ConnectivityService.this) {
+ if (msg.arg1 == mNetTransitionWakeLockSerialNumber &&
+ mNetTransitionWakeLock.isHeld()) {
+ mNetTransitionWakeLock.release();
+ causedBy = mNetTransitionWakeLockCausedBy;
+ }
+ }
+ if (causedBy != null) {
+ log("NetTransition Wakelock for " + causedBy + " released by timeout");
+ }
break;
case EVENT_RESTORE_DEFAULT_NETWORK:
FeatureUser u = (FeatureUser)msg.obj;
@@ -1592,6 +1830,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
handleSetMobileData(enabled);
break;
}
+ case EVENT_APPLY_GLOBAL_HTTP_PROXY:
+ {
+ handleDeprecatedGlobalHttpProxy();
+ }
}
}
}
@@ -1648,6 +1890,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
+ public String[] getTetherableBluetoothRegexs() {
+ enforceTetherAccessPermission();
+ if (isTetheringSupported()) {
+ return mTethering.getTetherableBluetoothRegexs();
+ } else {
+ return new String[0];
+ }
+ }
+
// TODO - move iface listing, queries, etc to new module
// javadoc from interface
public String[] getTetherableIfaces() {
@@ -1676,9 +1927,28 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return tetherEnabledInSettings && mTetheringConfigValid;
}
+ // An API NetworkStateTrackers can call when they lose their network.
+ // This will automatically be cleared after X seconds or a network becomes CONNECTED,
+ // whichever happens first. The timer is started by the first caller and not
+ // restarted by subsequent callers.
+ public void requestNetworkTransitionWakelock(String forWhom) {
+ enforceConnectivityInternalPermission();
+ synchronized (this) {
+ if (mNetTransitionWakeLock.isHeld()) return;
+ mNetTransitionWakeLockSerialNumber++;
+ mNetTransitionWakeLock.acquire();
+ mNetTransitionWakeLockCausedBy = forWhom;
+ }
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+ mNetTransitionWakeLockSerialNumber, 0),
+ mNetTransitionWakeLockTimeout);
+ return;
+ }
+
// 100 percent is full good, 0 is full bad.
public void reportInetCondition(int networkType, int percentage) {
- if (DBG) Slog.d(TAG, "reportNetworkCondition(" + networkType + ", " + percentage + ")");
+ if (DBG) log("reportNetworkCondition(" + networkType + ", " + percentage + ")");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.STATUS_BAR,
"ConnectivityService");
@@ -1700,22 +1970,22 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void handleInetConditionChange(int netType, int condition) {
if (DBG) {
- Slog.d(TAG, "Inet connectivity change, net=" +
+ log("Inet connectivity change, net=" +
netType + ", condition=" + condition +
",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
}
if (mActiveDefaultNetwork == -1) {
- if (DBG) Slog.d(TAG, "no active default network - aborting");
+ if (DBG) log("no active default network - aborting");
return;
}
if (mActiveDefaultNetwork != netType) {
- if (DBG) Slog.d(TAG, "given net not default - aborting");
+ if (DBG) log("given net not default - aborting");
return;
}
mDefaultInetCondition = condition;
int delay;
if (mInetConditionChangeInFlight == false) {
- if (DBG) Slog.d(TAG, "starting a change hold");
+ if (DBG) log("starting a change hold");
// setup a new hold to debounce this
if (mDefaultInetCondition > 50) {
delay = Settings.Secure.getInt(mContext.getContentResolver(),
@@ -1730,37 +2000,169 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} else {
// we've set the new condition, when this hold ends that will get
// picked up
- if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt");
+ if (DBG) log("currently in hold - not setting new end evt");
}
}
private void handleInetConditionHoldEnd(int netType, int sequence) {
if (DBG) {
- Slog.d(TAG, "Inet hold end, net=" + netType +
+ log("Inet hold end, net=" + netType +
", condition =" + mDefaultInetCondition +
", published condition =" + mDefaultInetConditionPublished);
}
mInetConditionChangeInFlight = false;
if (mActiveDefaultNetwork == -1) {
- if (DBG) Slog.d(TAG, "no active default network - aborting");
+ if (DBG) log("no active default network - aborting");
return;
}
if (mDefaultConnectionSequence != sequence) {
- if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting");
+ if (DBG) log("event hold for obsolete network - aborting");
return;
}
if (mDefaultInetConditionPublished == mDefaultInetCondition) {
- if (DBG) Slog.d(TAG, "no change in condition - aborting");
+ if (DBG) log("no change in condition - aborting");
return;
}
NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
if (networkInfo.isConnected() == false) {
- if (DBG) Slog.d(TAG, "default network not connected - aborting");
+ if (DBG) log("default network not connected - aborting");
return;
}
mDefaultInetConditionPublished = mDefaultInetCondition;
sendInetConditionBroadcast(networkInfo);
return;
}
+
+ public synchronized ProxyProperties getProxy() {
+ if (mGlobalProxy != null) return mGlobalProxy;
+ if (mDefaultProxy != null) return mDefaultProxy;
+ return null;
+ }
+
+ public void setGlobalProxy(ProxyProperties proxyProperties) {
+ enforceChangePermission();
+ synchronized (mGlobalProxyLock) {
+ if (proxyProperties == mGlobalProxy) return;
+ if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
+ if (mGlobalProxy != null && mGlobalProxy.equals(proxyProperties)) return;
+
+ String host = "";
+ int port = 0;
+ String exclList = "";
+ if (proxyProperties != null && !TextUtils.isEmpty(proxyProperties.getHost())) {
+ mGlobalProxy = new ProxyProperties(proxyProperties);
+ host = mGlobalProxy.getHost();
+ port = mGlobalProxy.getPort();
+ exclList = mGlobalProxy.getExclusionList();
+ } else {
+ mGlobalProxy = null;
+ }
+ ContentResolver res = mContext.getContentResolver();
+ Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, host);
+ Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, port);
+ Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+ exclList);
+ }
+
+ if (mGlobalProxy == null) {
+ proxyProperties = mDefaultProxy;
+ }
+ sendProxyBroadcast(proxyProperties);
+ }
+
+ private void loadGlobalProxy() {
+ ContentResolver res = mContext.getContentResolver();
+ String host = Settings.Secure.getString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST);
+ int port = Settings.Secure.getInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, 0);
+ String exclList = Settings.Secure.getString(res,
+ Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+ if (!TextUtils.isEmpty(host)) {
+ ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList);
+ synchronized (mGlobalProxyLock) {
+ mGlobalProxy = proxyProperties;
+ }
+ }
+ }
+
+ public ProxyProperties getGlobalProxy() {
+ synchronized (mGlobalProxyLock) {
+ return mGlobalProxy;
+ }
+ }
+
+ private void handleApplyDefaultProxy(int type) {
+ // check if new default - push it out to all VM if so
+ ProxyProperties proxy = mNetTrackers[type].getLinkProperties().getHttpProxy();
+ synchronized (this) {
+ if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return;
+ if (mDefaultProxy == proxy) return;
+ if (!TextUtils.isEmpty(proxy.getHost())) {
+ mDefaultProxy = proxy;
+ } else {
+ mDefaultProxy = null;
+ }
+ }
+ if (DBG) log("changing default proxy to " + proxy);
+ if ((proxy == null && mGlobalProxy == null) || proxy.equals(mGlobalProxy)) return;
+ if (mGlobalProxy != null) return;
+ sendProxyBroadcast(proxy);
+ }
+
+ private void handleDeprecatedGlobalHttpProxy() {
+ String proxy = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.HTTP_PROXY);
+ if (!TextUtils.isEmpty(proxy)) {
+ String data[] = proxy.split(":");
+ String proxyHost = data[0];
+ int proxyPort = 8080;
+ if (data.length > 1) {
+ try {
+ proxyPort = Integer.parseInt(data[1]);
+ } catch (NumberFormatException e) {
+ return;
+ }
+ }
+ ProxyProperties p = new ProxyProperties(data[0], proxyPort, "");
+ setGlobalProxy(p);
+ }
+ }
+
+ private void sendProxyBroadcast(ProxyProperties proxy) {
+ if (proxy == null) proxy = new ProxyProperties("", 0, "");
+ log("sending Proxy Broadcast for " + proxy);
+ Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private static class SettingsObserver extends ContentObserver {
+ private int mWhat;
+ private Handler mHandler;
+ SettingsObserver(Handler handler, int what) {
+ super(handler);
+ mHandler = handler;
+ mWhat = what;
+ }
+
+ void observe(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.HTTP_PROXY), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.obtainMessage(mWhat).sendToTarget();
+ }
+ }
+
+ private void log(String s) {
+ Slog.d(TAG, s);
+ }
+
+ private void loge(String s) {
+ Slog.e(TAG, s);
+ }
}
diff --git a/services/java/com/android/server/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java
new file mode 100644
index 0000000..3081ebe
--- /dev/null
+++ b/services/java/com/android/server/CountryDetectorService.java
@@ -0,0 +1,204 @@
+/*
+ * 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;
+
+import java.util.HashMap;
+
+import com.android.server.location.ComprehensiveCountryDetector;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.ICountryDetector;
+import android.location.ICountryListener;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * This class detects the country that the user is in through
+ * {@link ComprehensiveCountryDetector}.
+ *
+ * @hide
+ */
+public class CountryDetectorService extends ICountryDetector.Stub implements Runnable {
+
+ /**
+ * The class represents the remote listener, it will also removes itself
+ * from listener list when the remote process was died.
+ */
+ private final class Receiver implements IBinder.DeathRecipient {
+ private final ICountryListener mListener;
+ private final IBinder mKey;
+
+ public Receiver(ICountryListener listener) {
+ mListener = listener;
+ mKey = listener.asBinder();
+ }
+
+ public void binderDied() {
+ removeListener(mKey);
+ }
+
+ @Override
+ public boolean equals(Object otherObj) {
+ if (otherObj instanceof Receiver) {
+ return mKey.equals(((Receiver) otherObj).mKey);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mKey.hashCode();
+ }
+
+ public ICountryListener getListener() {
+ return mListener;
+ }
+ }
+
+ private final static String TAG = "CountryDetectorService";
+
+ private final HashMap<IBinder, Receiver> mReceivers;
+ private final Context mContext;
+ private ComprehensiveCountryDetector mCountryDetector;
+ private boolean mSystemReady;
+ private Handler mHandler;
+ private CountryListener mLocationBasedDetectorListener;
+
+ public CountryDetectorService(Context context) {
+ super();
+ mReceivers = new HashMap<IBinder, Receiver>();
+ mContext = context;
+ }
+
+ @Override
+ public Country detectCountry() throws RemoteException {
+ if (!mSystemReady) {
+ throw new RemoteException();
+ }
+ return mCountryDetector.detectCountry();
+ }
+
+ /**
+ * Add the ICountryListener into the listener list.
+ */
+ @Override
+ public void addCountryListener(ICountryListener listener) throws RemoteException {
+ if (!mSystemReady) {
+ throw new RemoteException();
+ }
+ addListener(listener);
+ }
+
+ /**
+ * Remove the ICountryListener from the listener list.
+ */
+ @Override
+ public void removeCountryListener(ICountryListener listener) throws RemoteException {
+ if (!mSystemReady) {
+ throw new RemoteException();
+ }
+ removeListener(listener.asBinder());
+ }
+
+ private void addListener(ICountryListener listener) {
+ synchronized (mReceivers) {
+ Receiver r = new Receiver(listener);
+ try {
+ listener.asBinder().linkToDeath(r, 0);
+ mReceivers.put(listener.asBinder(), r);
+ if (mReceivers.size() == 1) {
+ Slog.d(TAG, "The first listener is added");
+ setCountryListener(mLocationBasedDetectorListener);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "linkToDeath failed:", e);
+ }
+ }
+ }
+
+ private void removeListener(IBinder key) {
+ synchronized (mReceivers) {
+ mReceivers.remove(key);
+ if (mReceivers.isEmpty()) {
+ setCountryListener(null);
+ Slog.d(TAG, "No listener is left");
+ }
+ }
+ }
+
+
+ protected void notifyReceivers(Country country) {
+ synchronized(mReceivers) {
+ for (Receiver receiver : mReceivers.values()) {
+ try {
+ receiver.getListener().onCountryDetected(country);
+ } catch (RemoteException e) {
+ // TODO: Shall we remove the receiver?
+ Slog.e(TAG, "notifyReceivers failed:", e);
+ }
+ }
+ }
+ }
+
+ void systemReady() {
+ // Shall we wait for the initialization finish.
+ Thread thread = new Thread(this, "CountryDetectorService");
+ thread.start();
+ }
+
+ private void initialize() {
+ mCountryDetector = new ComprehensiveCountryDetector(mContext);
+ mLocationBasedDetectorListener = new CountryListener() {
+ public void onCountryDetected(final Country country) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ notifyReceivers(country);
+ }
+ });
+ }
+ };
+ }
+
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ Looper.prepare();
+ mHandler = new Handler();
+ initialize();
+ mSystemReady = true;
+ Looper.loop();
+ }
+
+ protected void setCountryListener(final CountryListener listener) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCountryDetector.setCountryListener(listener);
+ }
+ });
+ }
+
+ // For testing
+ boolean isSystemReady() {
+ return mSystemReady;
+ }
+}
diff --git a/services/java/com/android/server/DemoDataSet.java b/services/java/com/android/server/DemoDataSet.java
deleted file mode 100644
index 277985f..0000000
--- a/services/java/com/android/server/DemoDataSet.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2007 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;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.AssetManager;
-import android.net.Uri;
-import android.os.Environment;
-import android.provider.Contacts;
-import android.provider.Settings;
-import android.provider.MediaStore.Images;
-import android.util.Config;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class DemoDataSet
-{
- private final static String LOG_TAG = "DemoDataSet";
-
- private ContentResolver mContentResolver;
-
- public final void add(Context context)
- {
- mContentResolver = context.getContentResolver();
-
- // Remove all the old data
- mContentResolver.delete(Contacts.People.CONTENT_URI, null, null);
-
- // Add the new data
- addDefaultData();
-
- // Add images from /android/images
- addDefaultImages();
- }
-
- private final void addDefaultImages()
- {
- File rootDirectory = Environment.getRootDirectory();
- String [] files
- = new File(rootDirectory, "images").list();
- int count = files.length;
-
- if (count == 0) {
- Slog.i(LOG_TAG, "addDefaultImages: no images found!");
- return;
- }
-
- for (int i = 0; i < count; i++)
- {
- String name = files[i];
- String path = rootDirectory + "/" + name;
-
- try {
- Images.Media.insertImage(mContentResolver, path, name, null);
- } catch (FileNotFoundException e) {
- Slog.e(LOG_TAG, "Failed to import image " + path, e);
- }
- }
- }
-
- private final void addDefaultData()
- {
- Slog.i(LOG_TAG, "Adding default data...");
-
-// addImage("Violet", "images/violet.png");
-// addImage("Corky", "images/corky.png");
-
- // PENDING: should this be done here?!?!
- Intent intent = new Intent(
- Intent.ACTION_CALL, Uri.fromParts("voicemail", "", null));
- addShortcut("1", intent);
- }
-
- private final Uri addImage(String name, Uri file)
- {
- ContentValues imagev = new ContentValues();
- imagev.put("name", name);
-
- Uri url = null;
-
- AssetManager ass = AssetManager.getSystem();
- InputStream in = null;
- OutputStream out = null;
-
- try
- {
- in = ass.open(file.toString());
-
- url = mContentResolver.insert(Images.Media.INTERNAL_CONTENT_URI, imagev);
- out = mContentResolver.openOutputStream(url);
-
- final int size = 8 * 1024;
- byte[] buf = new byte[size];
-
- int count = 0;
- do
- {
- count = in.read(buf, 0, size);
- if (count > 0) {
- out.write(buf, 0, count);
- }
- } while (count > 0);
- }
- catch (Exception e)
- {
- Slog.e(LOG_TAG, "Failed to insert image '" + file + "'", e);
- url = null;
- }
-
- return url;
- }
-
- private final Uri addShortcut(String shortcut, Intent intent)
- {
- if (Config.LOGV) Slog.v(LOG_TAG, "addShortcut: shortcut=" + shortcut + ", intent=" + intent);
- return Settings.Bookmarks.add(mContentResolver, intent, null, null,
- shortcut != null ? shortcut.charAt(0) : 0, 0);
- }
-}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 1538003..b2d534b 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -28,18 +28,23 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.PowerManager;
@@ -48,9 +53,11 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.util.Slog;
+import android.os.SystemProperties;
+import android.provider.Settings;
import android.util.PrintWriterPrinter;
import android.util.Printer;
+import android.util.Slog;
import android.util.Xml;
import android.view.WindowManagerPolicy;
@@ -61,47 +68,97 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
+import java.util.Set;
/**
* Implementation of the device policy APIs.
*/
public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ private static final int REQUEST_EXPIRE_PASSWORD = 5571;
+
static final String TAG = "DevicePolicyManagerService";
-
+
+ private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * 86400 * 1000; // 5 days, in ms
+
+ protected static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
+ = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
+
+ private static final long MS_PER_DAY = 86400 * 1000;
+
final Context mContext;
final MyPackageMonitor mMonitor;
final PowerManager.WakeLock mWakeLock;
IPowerManager mIPowerManager;
-
+
int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
int mActivePasswordLength = 0;
+ int mActivePasswordUpperCase = 0;
+ int mActivePasswordLowerCase = 0;
+ int mActivePasswordLetters = 0;
+ int mActivePasswordNumeric = 0;
+ int mActivePasswordSymbols = 0;
+ int mActivePasswordNonLetter = 0;
int mFailedPasswordAttempts = 0;
-
+
int mPasswordOwner = -1;
-
+ Handler mHandler = new Handler();
+
final HashMap<ComponentName, ActiveAdmin> mAdminMap
= new HashMap<ComponentName, ActiveAdmin>();
final ArrayList<ActiveAdmin> mAdminList
= new ArrayList<ActiveAdmin>();
-
+
+ BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_BOOT_COMPLETED.equals(action)
+ || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
+ Slog.v(TAG, "Sending password expiration notifications for action " + action);
+ mHandler.post(new Runnable() {
+ public void run() {
+ handlePasswordExpirationNotification();
+ }
+ });
+ }
+ }
+ };
+
static class ActiveAdmin {
final DeviceAdminInfo info;
-
+
int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
int minimumPasswordLength = 0;
+ int passwordHistoryLength = 0;
+ int minimumPasswordUpperCase = 0;
+ int minimumPasswordLowerCase = 0;
+ int minimumPasswordLetters = 1;
+ int minimumPasswordNumeric = 1;
+ int minimumPasswordSymbols = 1;
+ int minimumPasswordNonLetter = 0;
long maximumTimeToUnlock = 0;
int maximumFailedPasswordsForWipe = 0;
-
+ long passwordExpirationTimeout = 0L;
+ long passwordExpirationDate = 0L;
+ boolean encryptionRequested = false;
+
+ // TODO: review implementation decisions with frameworks team
+ boolean specifiesGlobalProxy = false;
+ String globalProxySpec = null;
+ String globalProxyExclusionList = null;
+
ActiveAdmin(DeviceAdminInfo _info) {
info = _info;
}
-
+
int getUid() { return info.getActivityInfo().applicationInfo.uid; }
-
+
void writeToXml(XmlSerializer out)
throws IllegalArgumentException, IllegalStateException, IOException {
out.startTag(null, "policies");
@@ -114,10 +171,45 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (minimumPasswordLength > 0) {
out.startTag(null, "min-password-length");
out.attribute(null, "value", Integer.toString(minimumPasswordLength));
- out.endTag(null, "mn-password-length");
+ out.endTag(null, "min-password-length");
+ }
+ if(passwordHistoryLength > 0) {
+ out.startTag(null, "password-history-length");
+ out.attribute(null, "value", Integer.toString(passwordHistoryLength));
+ out.endTag(null, "password-history-length");
+ }
+ if (minimumPasswordUpperCase > 0) {
+ out.startTag(null, "min-password-uppercase");
+ out.attribute(null, "value", Integer.toString(minimumPasswordUpperCase));
+ out.endTag(null, "min-password-uppercase");
+ }
+ if (minimumPasswordLowerCase > 0) {
+ out.startTag(null, "min-password-lowercase");
+ out.attribute(null, "value", Integer.toString(minimumPasswordLowerCase));
+ out.endTag(null, "min-password-lowercase");
+ }
+ if (minimumPasswordLetters > 0) {
+ out.startTag(null, "min-password-letters");
+ out.attribute(null, "value", Integer.toString(minimumPasswordLetters));
+ out.endTag(null, "min-password-letters");
+ }
+ if (minimumPasswordNumeric > 0) {
+ out.startTag(null, "min-password-numeric");
+ out.attribute(null, "value", Integer.toString(minimumPasswordNumeric));
+ out.endTag(null, "min-password-numeric");
+ }
+ if (minimumPasswordSymbols > 0) {
+ out.startTag(null, "min-password-symbols");
+ out.attribute(null, "value", Integer.toString(minimumPasswordSymbols));
+ out.endTag(null, "min-password-symbols");
+ }
+ if (minimumPasswordNonLetter > 0) {
+ out.startTag(null, "min-password-nonletter");
+ out.attribute(null, "value", Integer.toString(minimumPasswordNonLetter));
+ out.endTag(null, "min-password-nonletter");
}
}
- if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ if (maximumTimeToUnlock != 0) {
out.startTag(null, "max-time-to-unlock");
out.attribute(null, "value", Long.toString(maximumTimeToUnlock));
out.endTag(null, "max-time-to-unlock");
@@ -127,8 +219,38 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe));
out.endTag(null, "max-failed-password-wipe");
}
+ if (specifiesGlobalProxy) {
+ out.startTag(null, "specifies-global-proxy");
+ out.attribute(null, "value", Boolean.toString(specifiesGlobalProxy));
+ out.endTag(null, "specifies_global_proxy");
+ if (globalProxySpec != null) {
+ out.startTag(null, "global-proxy-spec");
+ out.attribute(null, "value", globalProxySpec);
+ out.endTag(null, "global-proxy-spec");
+ }
+ if (globalProxyExclusionList != null) {
+ out.startTag(null, "global-proxy-exclusion-list");
+ out.attribute(null, "value", globalProxyExclusionList);
+ out.endTag(null, "global-proxy-exclusion-list");
+ }
+ }
+ if (passwordExpirationTimeout != 0L) {
+ out.startTag(null, "password-expiration-timeout");
+ out.attribute(null, "value", Long.toString(passwordExpirationTimeout));
+ out.endTag(null, "password-expiration-timeout");
+ }
+ if (passwordExpirationDate != 0L) {
+ out.startTag(null, "password-expiration-date");
+ out.attribute(null, "value", Long.toString(passwordExpirationDate));
+ out.endTag(null, "password-expiration-date");
+ }
+ if (encryptionRequested) {
+ out.startTag(null, "encryption-requested");
+ out.attribute(null, "value", Boolean.toString(encryptionRequested));
+ out.endTag(null, "encryption-requested");
+ }
}
-
+
void readFromXml(XmlPullParser parser)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
@@ -147,19 +269,58 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else if ("min-password-length".equals(tag)) {
minimumPasswordLength = Integer.parseInt(
parser.getAttributeValue(null, "value"));
+ } else if ("password-history-length".equals(tag)) {
+ passwordHistoryLength = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
+ } else if ("min-password-uppercase".equals(tag)) {
+ minimumPasswordUpperCase = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
+ } else if ("min-password-lowercase".equals(tag)) {
+ minimumPasswordLowerCase = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
+ } else if ("min-password-letters".equals(tag)) {
+ minimumPasswordLetters = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
+ } else if ("min-password-numeric".equals(tag)) {
+ minimumPasswordNumeric = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
+ } else if ("min-password-symbols".equals(tag)) {
+ minimumPasswordSymbols = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
+ } else if ("min-password-nonletter".equals(tag)) {
+ minimumPasswordNonLetter = Integer.parseInt(
+ parser.getAttributeValue(null, "value"));
} else if ("max-time-to-unlock".equals(tag)) {
maximumTimeToUnlock = Long.parseLong(
parser.getAttributeValue(null, "value"));
} else if ("max-failed-password-wipe".equals(tag)) {
maximumFailedPasswordsForWipe = Integer.parseInt(
parser.getAttributeValue(null, "value"));
+ } else if ("specifies-global-proxy".equals(tag)) {
+ specifiesGlobalProxy = Boolean.parseBoolean(
+ parser.getAttributeValue(null, "value"));
+ } else if ("global-proxy-spec".equals(tag)) {
+ globalProxySpec =
+ parser.getAttributeValue(null, "value");
+ } else if ("global-proxy-exclusion-list".equals(tag)) {
+ globalProxyExclusionList =
+ parser.getAttributeValue(null, "value");
+ } else if ("password-expiration-timeout".equals(tag)) {
+ passwordExpirationTimeout = Long.parseLong(
+ parser.getAttributeValue(null, "value"));
+ } else if ("password-expiration-date".equals(tag)) {
+ passwordExpirationDate = Long.parseLong(
+ parser.getAttributeValue(null, "value"));
+ } else if ("encryption-requested".equals(tag)) {
+ encryptionRequested = Boolean.parseBoolean(
+ parser.getAttributeValue(null, "value"));
} else {
Slog.w(TAG, "Unknown admin tag: " + tag);
}
XmlUtils.skipCurrentTag(parser);
}
}
-
+
void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("uid="); pw.println(getUid());
pw.print(prefix); pw.println("policies:");
@@ -170,23 +331,54 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
pw.print(prefix); pw.print("passwordQuality=0x");
- pw.print(Integer.toHexString(passwordQuality));
- pw.print(" minimumPasswordLength=");
+ pw.println(Integer.toHexString(passwordQuality));
+ pw.print(prefix); pw.print("minimumPasswordLength=");
pw.println(minimumPasswordLength);
+ pw.print(prefix); pw.print("passwordHistoryLength=");
+ pw.println(passwordHistoryLength);
+ pw.print(prefix); pw.print("minimumPasswordUpperCase=");
+ pw.println(minimumPasswordUpperCase);
+ pw.print(prefix); pw.print("minimumPasswordLowerCase=");
+ pw.println(minimumPasswordLowerCase);
+ pw.print(prefix); pw.print("minimumPasswordLetters=");
+ pw.println(minimumPasswordLetters);
+ pw.print(prefix); pw.print("minimumPasswordNumeric=");
+ pw.println(minimumPasswordNumeric);
+ pw.print(prefix); pw.print("minimumPasswordSymbols=");
+ pw.println(minimumPasswordSymbols);
+ pw.print(prefix); pw.print("minimumPasswordNonLetter=");
+ pw.println(minimumPasswordNonLetter);
pw.print(prefix); pw.print("maximumTimeToUnlock=");
pw.println(maximumTimeToUnlock);
pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
pw.println(maximumFailedPasswordsForWipe);
+ pw.print(prefix); pw.print("specifiesGlobalProxy=");
+ pw.println(specifiesGlobalProxy);
+ pw.print(prefix); pw.print("passwordExpirationTimeout=");
+ pw.println(passwordExpirationTimeout);
+ pw.print(prefix); pw.print("passwordExpirationDate=");
+ pw.println(passwordExpirationDate);
+ if (globalProxySpec != null) {
+ pw.print(prefix); pw.print("globalProxySpec=");
+ pw.println(globalProxySpec);
+ }
+ if (globalProxyExclusionList != null) {
+ pw.print(prefix); pw.print("globalProxyEclusionList=");
+ pw.println(globalProxyExclusionList);
+ }
+ pw.print(prefix); pw.print("encryptionRequested=");
+ pw.println(encryptionRequested);
}
}
-
+
class MyPackageMonitor extends PackageMonitor {
+ @Override
public void onSomePackagesChanged() {
synchronized (DevicePolicyManagerService.this) {
boolean removed = false;
for (int i=mAdminList.size()-1; i>=0; i--) {
ActiveAdmin aa = mAdminList.get(i);
- int change = isPackageDisappearing(aa.info.getPackageName());
+ int change = isPackageDisappearing(aa.info.getPackageName());
if (change == PACKAGE_PERMANENT_CHANGE
|| change == PACKAGE_TEMPORARY_CHANGE) {
Slog.w(TAG, "Admin unexpectedly uninstalled: "
@@ -211,7 +403,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
/**
* Instantiates the service.
*/
@@ -221,6 +413,50 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mMonitor.register(context, true);
mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM");
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+ filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION);
+ context.registerReceiver(mReceiver, filter);
+ }
+
+ /**
+ * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
+ * reminders. Clears alarm if no expirations are configured.
+ */
+ protected void setExpirationAlarmCheckLocked(Context context) {
+ final long expiration = getPasswordExpirationLocked(null);
+ final long now = System.currentTimeMillis();
+ final long timeToExpire = expiration - now;
+ final long alarmTime;
+ if (expiration == 0) {
+ // No expirations are currently configured: Cancel alarm.
+ alarmTime = 0;
+ } else if (timeToExpire <= 0) {
+ // The password has already expired: Repeat every 24 hours.
+ alarmTime = now + MS_PER_DAY;
+ } else {
+ // Selecting the next alarm time: Roll forward to the next 24 hour multiple before
+ // the expiration time.
+ long alarmInterval = timeToExpire % MS_PER_DAY;
+ if (alarmInterval == 0) {
+ alarmInterval = MS_PER_DAY;
+ }
+ alarmTime = now + alarmInterval;
+ }
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ PendingIntent pi = PendingIntent.getBroadcast(context, REQUEST_EXPIRE_PASSWORD,
+ new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+ am.cancel(pi);
+ if (alarmTime != 0) {
+ am.set(AlarmManager.RTC, alarmTime, pi);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private IPowerManager getIPowerManager() {
@@ -230,7 +466,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
return mIPowerManager;
}
-
+
ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who) {
ActiveAdmin admin = mAdminMap.get(who);
if (admin != null
@@ -240,7 +476,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
return null;
}
-
+
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
throws SecurityException {
final int callingUid = Binder.getCallingUid();
@@ -271,13 +507,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ Binder.getCallingUid() + " for policy #" + reqPolicy);
}
}
-
+
void sendAdminCommandLocked(ActiveAdmin admin, String action) {
Intent intent = new Intent(action);
intent.setComponent(admin.info.getComponent());
+ if (action.equals(DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING)) {
+ intent.putExtra("expiration", admin.passwordExpirationDate);
+ }
mContext.sendBroadcast(intent);
}
-
+
void sendAdminCommandLocked(String action, int reqPolicy) {
final int N = mAdminList.size();
if (N > 0) {
@@ -289,19 +528,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
void removeActiveAdminLocked(ComponentName adminReceiver) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
if (admin != null) {
+ boolean doProxyCleanup =
+ admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
sendAdminCommandLocked(admin,
DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED);
// XXX need to wait for it to complete.
mAdminList.remove(admin);
mAdminMap.remove(adminReceiver);
validatePasswordOwnerLocked();
+ if (doProxyCleanup) {
+ resetGlobalProxy();
+ }
}
}
-
+
public DeviceAdminInfo findAdmin(ComponentName adminName) {
Intent resolveIntent = new Intent();
resolveIntent.setComponent(adminName);
@@ -310,7 +554,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (infos == null || infos.size() <= 0) {
throw new IllegalArgumentException("Unknown admin: " + adminName);
}
-
+
try {
return new DeviceAdminInfo(mContext, infos.get(0));
} catch (XmlPullParserException e) {
@@ -321,7 +565,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return null;
}
}
-
+
private static JournaledFile makeJournaledFile() {
final String base = "/data/system/device_policies.xml";
return new JournaledFile(new File(base), new File(base + ".tmp"));
@@ -337,7 +581,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.startDocument(null, true);
out.startTag(null, "policies");
-
+
final int N = mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin ap = mAdminList.get(i);
@@ -348,26 +592,36 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.endTag(null, "admin");
}
}
-
+
if (mPasswordOwner >= 0) {
out.startTag(null, "password-owner");
out.attribute(null, "value", Integer.toString(mPasswordOwner));
out.endTag(null, "password-owner");
}
-
+
if (mFailedPasswordAttempts != 0) {
out.startTag(null, "failed-password-attempts");
out.attribute(null, "value", Integer.toString(mFailedPasswordAttempts));
out.endTag(null, "failed-password-attempts");
}
-
- if (mActivePasswordQuality != 0 || mActivePasswordLength != 0) {
+
+ if (mActivePasswordQuality != 0 || mActivePasswordLength != 0
+ || mActivePasswordUpperCase != 0 || mActivePasswordLowerCase != 0
+ || mActivePasswordLetters != 0 || mActivePasswordNumeric != 0
+ || mActivePasswordSymbols != 0 || mActivePasswordNonLetter != 0) {
out.startTag(null, "active-password");
out.attribute(null, "quality", Integer.toString(mActivePasswordQuality));
out.attribute(null, "length", Integer.toString(mActivePasswordLength));
+ out.attribute(null, "uppercase", Integer.toString(mActivePasswordUpperCase));
+ out.attribute(null, "lowercase", Integer.toString(mActivePasswordLowerCase));
+ out.attribute(null, "letters", Integer.toString(mActivePasswordLetters));
+ out.attribute(null, "numeric", Integer
+ .toString(mActivePasswordNumeric));
+ out.attribute(null, "symbols", Integer.toString(mActivePasswordSymbols));
+ out.attribute(null, "nonletter", Integer.toString(mActivePasswordNonLetter));
out.endTag(null, "active-password");
}
-
+
out.endTag(null, "policies");
out.endDocument();
@@ -445,6 +699,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
parser.getAttributeValue(null, "quality"));
mActivePasswordLength = Integer.parseInt(
parser.getAttributeValue(null, "length"));
+ mActivePasswordUpperCase = Integer.parseInt(
+ parser.getAttributeValue(null, "uppercase"));
+ mActivePasswordLowerCase = Integer.parseInt(
+ parser.getAttributeValue(null, "lowercase"));
+ mActivePasswordLetters = Integer.parseInt(
+ parser.getAttributeValue(null, "letters"));
+ mActivePasswordNumeric = Integer.parseInt(
+ parser.getAttributeValue(null, "numeric"));
+ mActivePasswordSymbols = Integer.parseInt(
+ parser.getAttributeValue(null, "symbols"));
+ mActivePasswordNonLetter = Integer.parseInt(
+ parser.getAttributeValue(null, "nonletter"));
XmlUtils.skipCurrentTag(parser);
} else {
Slog.w(TAG, "Unknown tag: " + tag);
@@ -484,10 +750,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ Integer.toHexString(utils.getActivePasswordQuality()));
mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
mActivePasswordLength = 0;
+ mActivePasswordUpperCase = 0;
+ mActivePasswordLowerCase = 0;
+ mActivePasswordLetters = 0;
+ mActivePasswordNumeric = 0;
+ mActivePasswordSymbols = 0;
+ mActivePasswordNonLetter = 0;
}
-
+
validatePasswordOwnerLocked();
-
+
long timeMs = getMaximumTimeToLock(null);
if (timeMs <= 0) {
timeMs = Integer.MAX_VALUE;
@@ -506,12 +778,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
return;
}
throw new IllegalArgumentException("Invalid quality constant: 0x"
+ Integer.toHexString(quality));
}
-
+
void validatePasswordOwnerLocked() {
if (mPasswordOwner >= 0) {
boolean haveOwner = false;
@@ -528,17 +801,41 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
public void systemReady() {
synchronized (this) {
loadSettingsLocked();
}
}
-
- public void setActiveAdmin(ComponentName adminReceiver) {
+
+ private void handlePasswordExpirationNotification() {
+ synchronized (this) {
+ final long now = System.currentTimeMillis();
+ final int N = mAdminList.size();
+ if (N <= 0) {
+ return;
+ }
+ for (int i=0; i < N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
+ && admin.passwordExpirationTimeout > 0L
+ && admin.passwordExpirationDate > 0L
+ && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) {
+ sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
+ }
+ }
+ setExpirationAlarmCheckLocked(mContext);
+ }
+ }
+
+ /**
+ * @param adminReceiver The admin to add
+ * @param refreshing true = update an active admin, no error
+ */
+ public void setActiveAdmin(ComponentName adminReceiver, boolean refreshing) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-
+
DeviceAdminInfo info = findAdmin(adminReceiver);
if (info == null) {
throw new IllegalArgumentException("Bad admin: " + adminReceiver);
@@ -546,27 +843,51 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
- if (getActiveAdminUncheckedLocked(adminReceiver) != null) {
+ if (!refreshing && getActiveAdminUncheckedLocked(adminReceiver) != null) {
throw new IllegalArgumentException("Admin is already added");
}
- ActiveAdmin admin = new ActiveAdmin(info);
- mAdminMap.put(adminReceiver, admin);
- mAdminList.add(admin);
+ ActiveAdmin newAdmin = new ActiveAdmin(info);
+ mAdminMap.put(adminReceiver, newAdmin);
+ int replaceIndex = -1;
+ if (refreshing) {
+ final int N = mAdminList.size();
+ for (int i=0; i < N; i++) {
+ ActiveAdmin oldAdmin = mAdminList.get(i);
+ if (oldAdmin.info.getComponent().equals(adminReceiver)) {
+ replaceIndex = i;
+ break;
+ }
+ }
+ }
+ if (replaceIndex == -1) {
+ mAdminList.add(newAdmin);
+ } else {
+ mAdminList.set(replaceIndex, newAdmin);
+ }
saveSettingsLocked();
- sendAdminCommandLocked(admin,
- DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED);
+ sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
-
+
public boolean isAdminActive(ComponentName adminReceiver) {
synchronized (this) {
return getActiveAdminUncheckedLocked(adminReceiver) != null;
}
}
-
+
+ public boolean hasGrantedPolicy(ComponentName adminReceiver, int policyId) {
+ synchronized (this) {
+ ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver);
+ if (administrator == null) {
+ throw new SecurityException("No active admin " + adminReceiver);
+ }
+ return administrator.info.usesPolicy(policyId);
+ }
+ }
+
public List<ComponentName> getActiveAdmins() {
synchronized (this) {
final int N = mAdminList.size();
@@ -580,7 +901,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return res;
}
}
-
+
public boolean packageHasActiveAdmins(String packageName) {
synchronized (this) {
final int N = mAdminList.size();
@@ -592,7 +913,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
}
-
+
public void removeActiveAdmin(ComponentName adminReceiver) {
synchronized (this) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
@@ -611,10 +932,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
public void setPasswordQuality(ComponentName who, int quality) {
validateQualityConstant(quality);
-
+
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -627,16 +948,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
public int getPasswordQuality(ComponentName who) {
synchronized (this) {
int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-
+
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
return admin != null ? admin.passwordQuality : mode;
}
-
+
final int N = mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = mAdminList.get(i);
@@ -647,7 +968,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return mode;
}
}
-
+
public void setPasswordMinimumLength(ComponentName who, int length) {
synchronized (this) {
if (who == null) {
@@ -661,16 +982,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
public int getPasswordMinimumLength(ComponentName who) {
synchronized (this) {
int length = 0;
-
+
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
return admin != null ? admin.minimumPasswordLength : length;
}
-
+
final int N = mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = mAdminList.get(i);
@@ -681,18 +1002,343 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return length;
}
}
-
+
+ public void setPasswordHistoryLength(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.passwordHistoryLength != length) {
+ ap.passwordHistoryLength = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordHistoryLength(ComponentName who) {
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.passwordHistoryLength : length;
+ }
+
+ final int N = mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (length < admin.passwordHistoryLength) {
+ length = admin.passwordHistoryLength;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordExpirationTimeout(ComponentName who, long timeout) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ if (timeout < 0) {
+ throw new IllegalArgumentException("Timeout must be >= 0 ms");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD);
+ // Calling this API automatically bumps the expiration date
+ final long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
+ ap.passwordExpirationDate = expiration;
+ ap.passwordExpirationTimeout = timeout;
+ if (timeout > 0L) {
+ Slog.w(TAG, "setPasswordExpiration(): password will expire on "
+ + DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)
+ .format(new Date(expiration)));
+ }
+ saveSettingsLocked();
+ setExpirationAlarmCheckLocked(mContext); // in case this is the first one
+ }
+ }
+
+ /**
+ * Return a single admin's expiration cycle time, or the min of all cycle times.
+ * Returns 0 if not configured.
+ */
+ public long getPasswordExpirationTimeout(ComponentName who) {
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.passwordExpirationTimeout : 0L;
+ }
+
+ long timeout = 0L;
+ final int N = mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (timeout == 0L || (admin.passwordExpirationTimeout != 0L
+ && timeout > admin.passwordExpirationTimeout)) {
+ timeout = admin.passwordExpirationTimeout;
+ }
+ }
+ return timeout;
+ }
+ }
+
+ /**
+ * Return a single admin's expiration date/time, or the min (soonest) for all admins.
+ * Returns 0 if not configured.
+ */
+ private long getPasswordExpirationLocked(ComponentName who) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.passwordExpirationDate : 0L;
+ }
+
+ long timeout = 0L;
+ final int N = mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (timeout == 0L || (admin.passwordExpirationDate != 0
+ && timeout > admin.passwordExpirationDate)) {
+ timeout = admin.passwordExpirationDate;
+ }
+ }
+ return timeout;
+ }
+
+ public long getPasswordExpiration(ComponentName who) {
+ synchronized (this) {
+ return getPasswordExpirationLocked(who);
+ }
+ }
+
+ public void setPasswordMinimumUpperCase(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordUpperCase != length) {
+ ap.minimumPasswordUpperCase = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMinimumUpperCase(ComponentName who) {
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.minimumPasswordUpperCase : length;
+ }
+
+ final int N = mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (length < admin.minimumPasswordUpperCase) {
+ length = admin.minimumPasswordUpperCase;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumLowerCase(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordLowerCase != length) {
+ ap.minimumPasswordLowerCase = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMinimumLowerCase(ComponentName who) {
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.minimumPasswordLowerCase : length;
+ }
+
+ final int N = mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (length < admin.minimumPasswordLowerCase) {
+ length = admin.minimumPasswordLowerCase;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumLetters(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordLetters != length) {
+ ap.minimumPasswordLetters = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMinimumLetters(ComponentName who) {
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.minimumPasswordLetters : length;
+ }
+
+ final int N = mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (length < admin.minimumPasswordLetters) {
+ length = admin.minimumPasswordLetters;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumNumeric(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordNumeric != length) {
+ ap.minimumPasswordNumeric = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMinimumNumeric(ComponentName who) {
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.minimumPasswordNumeric : length;
+ }
+
+ final int N = mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (length < admin.minimumPasswordNumeric) {
+ length = admin.minimumPasswordNumeric;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumSymbols(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordSymbols != length) {
+ ap.minimumPasswordSymbols = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMinimumSymbols(ComponentName who) {
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.minimumPasswordSymbols : length;
+ }
+
+ final int N = mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (length < admin.minimumPasswordSymbols) {
+ length = admin.minimumPasswordSymbols;
+ }
+ }
+ return length;
+ }
+ }
+
+ public void setPasswordMinimumNonLetter(ComponentName who, int length) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (ap.minimumPasswordNonLetter != length) {
+ ap.minimumPasswordNonLetter = length;
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public int getPasswordMinimumNonLetter(ComponentName who) {
+ synchronized (this) {
+ int length = 0;
+
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return admin != null ? admin.minimumPasswordNonLetter : length;
+ }
+
+ final int N = mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (length < admin.minimumPasswordNonLetter) {
+ length = admin.minimumPasswordNonLetter;
+ }
+ }
+ return length;
+ }
+ }
+
public boolean isActivePasswordSufficient() {
synchronized (this) {
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
getActiveAdminForCallerLocked(null,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
- return mActivePasswordQuality >= getPasswordQuality(null)
- && mActivePasswordLength >= getPasswordMinimumLength(null);
+ if (mActivePasswordQuality < getPasswordQuality(null)
+ || mActivePasswordLength < getPasswordMinimumLength(null)) {
+ return false;
+ }
+ if(mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+ return true;
+ }
+ return mActivePasswordUpperCase >= getPasswordMinimumUpperCase(null)
+ && mActivePasswordLowerCase >= getPasswordMinimumLowerCase(null)
+ && mActivePasswordLetters >= getPasswordMinimumLetters(null)
+ && mActivePasswordNumeric >= getPasswordMinimumNumeric(null)
+ && mActivePasswordSymbols >= getPasswordMinimumSymbols(null)
+ && mActivePasswordNonLetter >= getPasswordMinimumNonLetter(null);
}
}
-
+
public int getCurrentFailedPasswordAttempts() {
synchronized (this) {
// This API can only be called by an active device admin,
@@ -702,7 +1348,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return mFailedPasswordAttempts;
}
}
-
+
public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) {
synchronized (this) {
// This API can only be called by an active device admin,
@@ -717,16 +1363,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
public int getMaximumFailedPasswordsForWipe(ComponentName who) {
synchronized (this) {
int count = 0;
-
+
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
return admin != null ? admin.maximumFailedPasswordsForWipe : count;
}
-
+
final int N = mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = mAdminList.get(i);
@@ -740,7 +1386,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return count;
}
}
-
+
public boolean resetPassword(String password, int flags) {
int quality;
synchronized (this) {
@@ -751,14 +1397,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
quality = getPasswordQuality(null);
if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
int realQuality = LockPatternUtils.computePasswordQuality(password);
- if (realQuality < quality) {
+ if (realQuality < quality
+ && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
Slog.w(TAG, "resetPassword: password quality 0x"
+ Integer.toHexString(quality)
+ " does not meet required quality 0x"
+ Integer.toHexString(quality));
return false;
}
- quality = realQuality;
+ quality = Math.max(realQuality, quality);
}
int length = getPasswordMinimumLength(null);
if (password.length() < length) {
@@ -766,14 +1413,86 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ " does not meet required length " + length);
return false;
}
+ if (quality == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+ int letters = 0;
+ int uppercase = 0;
+ int lowercase = 0;
+ int numbers = 0;
+ int symbols = 0;
+ int nonletter = 0;
+ for (int i = 0; i < password.length(); i++) {
+ char c = password.charAt(i);
+ if (c >= 'A' && c <= 'Z') {
+ letters++;
+ uppercase++;
+ } else if (c >= 'a' && c <= 'z') {
+ letters++;
+ lowercase++;
+ } else if (c >= '0' && c <= '9') {
+ numbers++;
+ nonletter++;
+ } else {
+ symbols++;
+ nonletter++;
+ }
+ }
+ int neededLetters = getPasswordMinimumLetters(null);
+ if(letters < neededLetters) {
+ Slog.w(TAG, "resetPassword: number of letters " + letters
+ + " does not meet required number of letters " + neededLetters);
+ return false;
+ }
+ int neededNumbers = getPasswordMinimumNumeric(null);
+ if (numbers < neededNumbers) {
+ Slog
+ .w(TAG, "resetPassword: number of numerical digits " + numbers
+ + " does not meet required number of numerical digits "
+ + neededNumbers);
+ return false;
+ }
+ int neededLowerCase = getPasswordMinimumLowerCase(null);
+ if (lowercase < neededLowerCase) {
+ Slog.w(TAG, "resetPassword: number of lowercase letters " + lowercase
+ + " does not meet required number of lowercase letters "
+ + neededLowerCase);
+ return false;
+ }
+ int neededUpperCase = getPasswordMinimumUpperCase(null);
+ if (uppercase < neededUpperCase) {
+ Slog.w(TAG, "resetPassword: number of uppercase letters " + uppercase
+ + " does not meet required number of uppercase letters "
+ + neededUpperCase);
+ return false;
+ }
+ int neededSymbols = getPasswordMinimumSymbols(null);
+ if (symbols < neededSymbols) {
+ Slog.w(TAG, "resetPassword: number of special symbols " + symbols
+ + " does not meet required number of special symbols " + neededSymbols);
+ return false;
+ }
+ int neededNonLetter = getPasswordMinimumNonLetter(null);
+ if (nonletter < neededNonLetter) {
+ Slog.w(TAG, "resetPassword: number of non-letter characters " + nonletter
+ + " does not meet required number of non-letter characters "
+ + neededNonLetter);
+ return false;
+ }
+ }
+
+ LockPatternUtils utils = new LockPatternUtils(mContext);
+ if(utils.checkPasswordHistory(password)) {
+ Slog.w(TAG, "resetPassword: password is the same as one of the last "
+ + getPasswordHistoryLength(null) + " passwords");
+ return false;
+ }
}
-
+
int callingUid = Binder.getCallingUid();
if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) {
Slog.w(TAG, "resetPassword: already set by another uid and not entered by user");
return false;
}
-
+
// Don't do this with the lock held, because it is going to call
// back in to the service.
long ident = Binder.clearCallingIdentity();
@@ -791,10 +1510,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} finally {
Binder.restoreCallingIdentity(ident);
}
-
+
return true;
}
-
+
public void setMaximumTimeToLock(ComponentName who, long timeMs) {
synchronized (this) {
if (who == null) {
@@ -804,16 +1523,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
if (ap.maximumTimeToUnlock != timeMs) {
ap.maximumTimeToUnlock = timeMs;
-
+
long ident = Binder.clearCallingIdentity();
try {
saveSettingsLocked();
-
+
timeMs = getMaximumTimeToLock(null);
if (timeMs <= 0) {
timeMs = Integer.MAX_VALUE;
}
-
+
try {
getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
} catch (RemoteException e) {
@@ -825,16 +1544,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
public long getMaximumTimeToLock(ComponentName who) {
synchronized (this) {
long time = 0;
-
+
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
return admin != null ? admin.maximumTimeToUnlock : time;
}
-
+
final int N = mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = mAdminList.get(i);
@@ -848,7 +1567,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return time;
}
}
-
+
public void lockNow() {
synchronized (this) {
// This API can only be called by an active device admin,
@@ -865,7 +1584,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
void wipeDataLocked(int flags) {
if ((flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0) {
Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
@@ -880,7 +1599,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
public void wipeData(int flags) {
synchronized (this) {
// This API can only be called by an active device admin,
@@ -895,11 +1614,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
public void getRemoveWarning(ComponentName comp, final RemoteCallback result) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-
+
synchronized (this) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(comp);
if (admin == null) {
@@ -922,22 +1641,34 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}, null, Activity.RESULT_OK, null, null);
}
}
-
- public void setActivePasswordState(int quality, int length) {
+
+ public void setActivePasswordState(int quality, int length, int letters, int uppercase,
+ int lowercase, int numbers, int symbols, int nonletter) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-
+
validateQualityConstant(quality);
-
+
synchronized (this) {
if (mActivePasswordQuality != quality || mActivePasswordLength != length
- || mFailedPasswordAttempts != 0) {
+ || mFailedPasswordAttempts != 0 || mActivePasswordLetters != letters
+ || mActivePasswordUpperCase != uppercase
+ || mActivePasswordLowerCase != lowercase || mActivePasswordNumeric != numbers
+ || mActivePasswordSymbols != symbols || mActivePasswordNonLetter != nonletter) {
long ident = Binder.clearCallingIdentity();
try {
mActivePasswordQuality = quality;
mActivePasswordLength = length;
+ mActivePasswordLetters = letters;
+ mActivePasswordLowerCase = lowercase;
+ mActivePasswordUpperCase = uppercase;
+ mActivePasswordNumeric = numbers;
+ mActivePasswordSymbols = symbols;
+ mActivePasswordNonLetter = nonletter;
mFailedPasswordAttempts = 0;
saveSettingsLocked();
+ updatePasswordExpirationsLocked();
+ setExpirationAlarmCheckLocked(mContext);
sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
} finally {
@@ -946,11 +1677,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
+ /**
+ * Called any time the device password is updated. Resets all password expiration clocks.
+ */
+ private void updatePasswordExpirationsLocked() {
+ final int N = mAdminList.size();
+ if (N > 0) {
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
+ long timeout = admin.passwordExpirationTimeout;
+ long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
+ admin.passwordExpirationDate = expiration;
+ }
+ }
+ saveSettingsLocked();
+ }
+ }
+
public void reportFailedPasswordAttempt() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-
+
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
@@ -967,11 +1716,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
public void reportSuccessfulPasswordAttempt() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-
+
synchronized (this) {
if (mFailedPasswordAttempts != 0 || mPasswordOwner >= 0) {
long ident = Binder.clearCallingIdentity();
@@ -987,7 +1736,212 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
-
+
+ public ComponentName setGlobalProxy(ComponentName who, String proxySpec,
+ String exclusionList) {
+ synchronized(this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
+
+ // Scan through active admins and find if anyone has already
+ // set the global proxy.
+ final int N = mAdminList.size();
+ Set<ComponentName> compSet = mAdminMap.keySet();
+ for (ComponentName component : compSet) {
+ ActiveAdmin ap = mAdminMap.get(component);
+ if ((ap.specifiesGlobalProxy) && (!component.equals(who))) {
+ // Another admin already sets the global proxy
+ // Return it to the caller.
+ return component;
+ }
+ }
+ if (proxySpec == null) {
+ admin.specifiesGlobalProxy = false;
+ admin.globalProxySpec = null;
+ admin.globalProxyExclusionList = null;
+ } else {
+
+ admin.specifiesGlobalProxy = true;
+ admin.globalProxySpec = proxySpec;
+ admin.globalProxyExclusionList = exclusionList;
+ }
+
+ // Reset the global proxy accordingly
+ // Do this using system permissions, as apps cannot write to secure settings
+ long origId = Binder.clearCallingIdentity();
+ resetGlobalProxy();
+ Binder.restoreCallingIdentity(origId);
+ return null;
+ }
+ }
+
+ public ComponentName getGlobalProxyAdmin() {
+ synchronized(this) {
+ // Scan through active admins and find if anyone has already
+ // set the global proxy.
+ final int N = mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin ap = mAdminList.get(i);
+ if (ap.specifiesGlobalProxy) {
+ // Device admin sets the global proxy
+ // Return it to the caller.
+ return ap.info.getComponent();
+ }
+ }
+ }
+ // No device admin sets the global proxy.
+ return null;
+ }
+
+ private void resetGlobalProxy() {
+ final int N = mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin ap = mAdminList.get(i);
+ if (ap.specifiesGlobalProxy) {
+ saveGlobalProxy(ap.globalProxySpec, ap.globalProxyExclusionList);
+ return;
+ }
+ }
+ // No device admins defining global proxies - reset global proxy settings to none
+ saveGlobalProxy(null, null);
+ }
+
+ private void saveGlobalProxy(String proxySpec, String exclusionList) {
+ if (exclusionList == null) {
+ exclusionList = "";
+ }
+ if (proxySpec == null) {
+ proxySpec = "";
+ }
+ // Remove white spaces
+ proxySpec = proxySpec.trim();
+ String data[] = proxySpec.split(":");
+ int proxyPort = 8080;
+ if (data.length > 1) {
+ try {
+ proxyPort = Integer.parseInt(data[1]);
+ } catch (NumberFormatException e) {}
+ }
+ exclusionList = exclusionList.trim();
+ ContentResolver res = mContext.getContentResolver();
+ Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_HOST, data[0]);
+ Settings.Secure.putInt(res, Settings.Secure.GLOBAL_HTTP_PROXY_PORT, proxyPort);
+ Settings.Secure.putString(res, Settings.Secure.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+ exclusionList);
+ }
+
+ /**
+ * Set the storage encryption request for a single admin. Returns the new total request
+ * status (for all admins).
+ */
+ public int setStorageEncryption(ComponentName who, boolean encrypt) {
+ synchronized (this) {
+ // Check for permissions
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_ENCRYPTED_STORAGE);
+
+ // Quick exit: If the filesystem does not support encryption, we can exit early.
+ if (!isEncryptionSupported()) {
+ return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
+ }
+
+ // (1) Record the value for the admin so it's sticky
+ if (ap.encryptionRequested != encrypt) {
+ ap.encryptionRequested = encrypt;
+ saveSettingsLocked();
+ }
+
+ // (2) Compute "max" for all admins
+ boolean newRequested = false;
+ final int N = mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ newRequested |= mAdminList.get(i).encryptionRequested;
+ }
+
+ // Notify OS of new request
+ setEncryptionRequested(newRequested);
+
+ // Return the new global request status
+ return newRequested
+ ? DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
+ : DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
+ }
+ }
+
+ /**
+ * Get the current storage encryption request status for a given admin, or aggregate of all
+ * active admins.
+ */
+ public boolean getStorageEncryption(ComponentName who) {
+ synchronized (this) {
+ // Check for permissions if a particular caller is specified
+ if (who != null) {
+ // When checking for a single caller, status is based on caller's request
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_ENCRYPTED_STORAGE);
+ return ap.encryptionRequested;
+ }
+
+ // If no particular caller is specified, return the aggregate set of requests.
+ // This is short circuited by returning true on the first hit.
+ final int N = mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ if (mAdminList.get(i).encryptionRequested) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Get the current encryption status of the device.
+ */
+ public int getStorageEncryptionStatus() {
+ return getEncryptionStatus();
+ }
+
+ /**
+ * Hook to low-levels: This should report if the filesystem supports encrypted storage.
+ */
+ private boolean isEncryptionSupported() {
+ // Note, this can be implemented as
+ // return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
+ // But is provided as a separate internal method if there's a faster way to do a
+ // simple check for supported-or-not.
+ return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
+ }
+
+ /**
+ * Hook to low-levels: Reporting the current status of encryption.
+ * @return A value such as {@link DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} or
+ * {@link DevicePolicyManager#ENCRYPTION_STATUS_INACTIVE} or
+ * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}.
+ */
+ private int getEncryptionStatus() {
+ String status = SystemProperties.get("ro.crypto.state", "unsupported");
+ if ("encrypted".equalsIgnoreCase(status)) {
+ return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
+ } else if ("unencrypted".equalsIgnoreCase(status)) {
+ return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
+ } else {
+ return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
+ }
+ }
+
+ /**
+ * Hook to low-levels: If needed, record the new admin setting for encryption.
+ */
+ private void setEncryptionRequested(boolean encrypt) {
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -998,12 +1952,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ ", uid=" + Binder.getCallingUid());
return;
}
-
+
final Printer p = new PrintWriterPrinter(pw);
-
+
synchronized (this) {
p.println("Current Device Policy Manager state:");
-
+
p.println(" Enabled Device Admins:");
final int N = mAdminList.size();
for (int i=0; i<N; i++) {
@@ -1014,11 +1968,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
ap.dump(" ", pw);
}
}
-
+
pw.println(" ");
pw.print(" mActivePasswordQuality=0x");
pw.println(Integer.toHexString(mActivePasswordQuality));
pw.print(" mActivePasswordLength="); pw.println(mActivePasswordLength);
+ pw.print(" mActivePasswordUpperCase="); pw.println(mActivePasswordUpperCase);
+ pw.print(" mActivePasswordLowerCase="); pw.println(mActivePasswordLowerCase);
+ pw.print(" mActivePasswordLetters="); pw.println(mActivePasswordLetters);
+ pw.print(" mActivePasswordNumeric="); pw.println(mActivePasswordNumeric);
+ pw.print(" mActivePasswordSymbols="); pw.println(mActivePasswordSymbols);
+ pw.print(" mActivePasswordNonLetter="); pw.println(mActivePasswordNonLetter);
pw.print(" mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts);
pw.print(" mPasswordOwner="); pw.println(mPasswordOwner);
}
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 0b1a4a3..0fba7c3 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -16,7 +16,6 @@
package com.android.server;
-import com.android.server.am.ActivityManagerService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -38,7 +37,6 @@ import android.provider.Settings;
import android.util.Config;
import android.util.EventLog;
import android.util.Slog;
-import android.provider.Settings;
/**
* This class implements a service to monitor the amount of disk
@@ -66,6 +64,7 @@ class DeviceStorageMonitorService extends Binder {
private static final int MONITOR_INTERVAL = 1; //in minutes
private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
+ private static final int DEFAULT_THRESHOLD_MAX_BYTES = 500*1024*1024; // 500MB
private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes
private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
@@ -271,13 +270,18 @@ class DeviceStorageMonitorService extends Binder {
* any way
*/
private long getMemThreshold() {
- int value = Settings.Secure.getInt(
+ long value = Settings.Secure.getInt(
mContentResolver,
Settings.Secure.SYS_STORAGE_THRESHOLD_PERCENTAGE,
DEFAULT_THRESHOLD_PERCENTAGE);
if(localLOGV) Slog.v(TAG, "Threshold Percentage="+value);
+ value *= mTotalMemory;
+ long maxValue = Settings.Secure.getInt(
+ mContentResolver,
+ Settings.Secure.SYS_STORAGE_THRESHOLD_MAX_BYTES,
+ DEFAULT_THRESHOLD_MAX_BYTES);
//evaluate threshold value
- return mTotalMemory*value;
+ return value < maxValue ? value : maxValue;
}
/*
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index bee8872..dea9007 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -102,8 +102,8 @@ class DockObserver extends UEventObserver {
try {
FileReader file = new FileReader(DOCK_STATE_PATH);
int len = file.read(buffer, 0, 1024);
+ file.close();
mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
-
} catch (FileNotFoundException e) {
Slog.w(TAG, "This kernel does not have dock station support");
} catch (Exception e) {
@@ -158,13 +158,17 @@ class DockObserver extends UEventObserver {
{
String whichSound = null;
if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+ if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+ (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+ (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
whichSound = Settings.System.DESK_UNDOCK_SOUND;
} else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
whichSound = Settings.System.CAR_UNDOCK_SOUND;
}
} else {
- if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+ if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+ (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+ (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
whichSound = Settings.System.DESK_DOCK_SOUND;
} else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
whichSound = Settings.System.CAR_DOCK_SOUND;
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index 0e45145..5c878c9 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -97,10 +97,18 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
// Ensure that all log entries have a unique timestamp
private long mLastTimestamp = 0;
+ private volatile boolean mBooted = false;
+
/** Receives events that might indicate a need to clean up files. */
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ if (intent != null && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+ mBooted = true;
+ return;
+ }
+
+ // Else, for ACTION_DEVICE_STORAGE_LOW:
mCachedQuotaUptimeMillis = 0; // Force a re-check of quota size
// Run the initialization in the background (not this main thread).
@@ -132,7 +140,11 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
// Set up intent receivers
mContext = context;
mContentResolver = context.getContentResolver();
- context.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW));
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
+ filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+ context.registerReceiver(mReceiver, filter);
mContentResolver.registerContentObserver(
Settings.Secure.CONTENT_URI, true,
@@ -218,8 +230,17 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
}
} while (read > 0);
- createEntry(temp, tag, flags);
+ long time = createEntry(temp, tag, flags);
temp = null;
+
+ Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
+ dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);
+ dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);
+ if (!mBooted) {
+ dropboxIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ }
+ mContext.sendBroadcast(dropboxIntent, android.Manifest.permission.READ_LOGS);
+
} catch (IOException e) {
Slog.e(TAG, "Can't write: " + tag, e);
} finally {
@@ -343,16 +364,17 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
if ((entry.flags & DropBoxManager.IS_TEXT) != 0 && (doPrint || !doFile)) {
DropBoxManager.Entry dbe = null;
+ InputStreamReader isr = null;
try {
dbe = new DropBoxManager.Entry(
entry.tag, entry.timestampMillis, entry.file, entry.flags);
if (doPrint) {
- InputStreamReader r = new InputStreamReader(dbe.getInputStream());
+ isr = new InputStreamReader(dbe.getInputStream());
char[] buf = new char[4096];
boolean newline = false;
for (;;) {
- int n = r.read(buf);
+ int n = isr.read(buf);
if (n <= 0) break;
out.append(buf, 0, n);
newline = (buf[n - 1] == '\n');
@@ -376,6 +398,12 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
Slog.e(TAG, "Can't read: " + entry.file, e);
} finally {
if (dbe != null) dbe.close();
+ if (isr != null) {
+ try {
+ isr.close();
+ } catch (IOException unused) {
+ }
+ }
}
}
@@ -597,7 +625,7 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
}
/** Moves a temporary file to a final log filename and enrolls it. */
- private synchronized void createEntry(File temp, String tag, int flags) throws IOException {
+ private synchronized long createEntry(File temp, String tag, int flags) throws IOException {
long t = System.currentTimeMillis();
// Require each entry to have a unique timestamp; if there are entries
@@ -636,6 +664,7 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
} else {
enrollEntry(new EntryFile(temp, mDropBoxDir, tag, t, flags, mBlockSize));
}
+ return t;
}
/**
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
deleted file mode 100644
index 6f0a91d..0000000
--- a/services/java/com/android/server/HeadsetObserver.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * 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;
-
-import android.app.ActivityManagerNative;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.UEventObserver;
-import android.util.Slog;
-import android.media.AudioManager;
-
-import java.io.FileReader;
-import java.io.FileNotFoundException;
-
-/**
- * <p>HeadsetObserver monitors for a wired headset.
- */
-class HeadsetObserver extends UEventObserver {
- private static final String TAG = HeadsetObserver.class.getSimpleName();
- private static final boolean LOG = true;
-
- private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w";
- private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
- private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name";
-
- private static final int BIT_HEADSET = (1 << 0);
- private static final int BIT_HEADSET_NO_MIC = (1 << 1);
- private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC);
- private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
-
- private int mHeadsetState;
- private int mPrevHeadsetState;
- private String mHeadsetName;
-
- private final Context mContext;
- private final WakeLock mWakeLock; // held while there is a pending route change
-
- public HeadsetObserver(Context context) {
- mContext = context;
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetObserver");
- mWakeLock.setReferenceCounted(false);
-
- startObserving(HEADSET_UEVENT_MATCH);
-
- init(); // set initial status
- }
-
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
-
- try {
- update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
- }
- }
-
- private synchronized final void init() {
- char[] buffer = new char[1024];
-
- String newName = mHeadsetName;
- int newState = mHeadsetState;
- mPrevHeadsetState = mHeadsetState;
- try {
- FileReader file = new FileReader(HEADSET_STATE_PATH);
- int len = file.read(buffer, 0, 1024);
- newState = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- file = new FileReader(HEADSET_NAME_PATH);
- len = file.read(buffer, 0, 1024);
- newName = new String(buffer, 0, len).trim();
-
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have wired headset support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
-
- update(newName, newState);
- }
-
- private synchronized final void update(String newName, int newState) {
- // Retain only relevant bits
- int headsetState = newState & SUPPORTED_HEADSETS;
- int newOrOld = headsetState | mHeadsetState;
- int delay = 0;
- // reject all suspect transitions: only accept state changes from:
- // - a: 0 heaset to 1 headset
- // - b: 1 headset to 0 headset
- if (mHeadsetState == headsetState || ((newOrOld & (newOrOld - 1)) != 0)) {
- return;
- }
-
- mHeadsetName = newName;
- mPrevHeadsetState = mHeadsetState;
- mHeadsetState = headsetState;
-
- if (headsetState == 0) {
- Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
- mContext.sendBroadcast(intent);
- // It can take hundreds of ms flush the audio pipeline after
- // apps pause audio playback, but audio route changes are
- // immediate, so delay the route change by 1000ms.
- // This could be improved once the audio sub-system provides an
- // interface to clear the audio pipeline.
- delay = 1000;
- } else {
- // Insert the same delay for headset connection so that the connection event is not
- // broadcast before the disconnection event in case of fast removal/insertion
- if (mHandler.hasMessages(0)) {
- delay = 1000;
- }
- }
- mWakeLock.acquire();
- mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
- mHeadsetState,
- mPrevHeadsetState,
- mHeadsetName),
- delay);
- }
-
- private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) {
- int allHeadsets = SUPPORTED_HEADSETS;
- for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
- if ((curHeadset & allHeadsets) != 0) {
- sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName);
- allHeadsets &= ~curHeadset;
- }
- }
- }
-
- private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
- if ((headsetState & headset) != (prevHeadsetState & headset)) {
- // Pack up the values and broadcast them to everyone
- Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- int state = 0;
- int microphone = 0;
-
- if ((headset & HEADSETS_WITH_MIC) != 0) {
- microphone = 1;
- }
- if ((headsetState & headset) != 0) {
- state = 1;
- }
- intent.putExtra("state", state);
- intent.putExtra("name", headsetName);
- intent.putExtra("microphone", microphone);
-
- if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
- // TODO: Should we require a permission?
- ActivityManagerNative.broadcastStickyIntent(intent, null);
- }
- }
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- sendIntents(msg.arg1, msg.arg2, (String)msg.obj);
- mWakeLock.release();
- }
- };
-}
diff --git a/services/java/com/android/server/InputApplication.java b/services/java/com/android/server/InputApplication.java
index 38420d4..ae09484 100644
--- a/services/java/com/android/server/InputApplication.java
+++ b/services/java/com/android/server/InputApplication.java
@@ -18,16 +18,19 @@ package com.android.server;
/**
* Describes input-related application properties for use by the input dispatcher.
- *
* @hide
*/
public final class InputApplication {
+ // Application handle.
+ public InputApplicationHandle inputApplicationHandle;
+
// Application name.
public String name;
-
+
// Dispatching timeout.
public long dispatchingTimeoutNanos;
-
- // The application window token.
- public Object token;
+
+ public void recycle() {
+ inputApplicationHandle = null;
+ }
}
diff --git a/services/java/com/android/server/InputApplicationHandle.java b/services/java/com/android/server/InputApplicationHandle.java
new file mode 100644
index 0000000..d396d11
--- /dev/null
+++ b/services/java/com/android/server/InputApplicationHandle.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+/**
+ * Functions as a handle for an application that can receive input.
+ * Enables the native input dispatcher to refer indirectly to the window manager's
+ * application window token.
+ * @hide
+ */
+public final class InputApplicationHandle {
+ // Pointer to the native input application handle.
+ // This field is lazily initialized via JNI.
+ @SuppressWarnings("unused")
+ private int ptr;
+
+ // The window manager's application window token.
+ public final WindowManagerService.AppWindowToken appWindowToken;
+
+ private native void nativeDispose();
+
+ public InputApplicationHandle(WindowManagerService.AppWindowToken appWindowToken) {
+ this.appWindowToken = appWindowToken;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ nativeDispose();
+ super.finalize();
+ }
+}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index ba39c57..8d249ff 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -19,10 +19,17 @@ package com.android.server;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.os.SystemProperties;
import android.util.Slog;
@@ -30,7 +37,9 @@ import android.util.Xml;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.KeyEvent;
import android.view.Surface;
+import android.view.WindowManager;
import java.io.BufferedReader;
import java.io.File;
@@ -41,7 +50,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Properties;
/*
* Wraps the C++ InputManager and provides its callbacks.
@@ -69,7 +77,7 @@ public class InputManager {
private static native boolean nativeHasKeys(int deviceId, int sourceMask,
int[] keyCodes, boolean[] keyExists);
private static native void nativeRegisterInputChannel(InputChannel inputChannel,
- boolean monitor);
+ InputWindowHandle inputWindowHandle, boolean monitor);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
private static native int nativeInjectInputEvent(InputEvent event,
int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
@@ -79,6 +87,8 @@ public class InputManager {
private static native InputDevice nativeGetInputDevice(int deviceId);
private static native void nativeGetInputConfiguration(Configuration configuration);
private static native int[] nativeGetInputDeviceIds();
+ private static native boolean nativeTransferTouchFocus(InputChannel fromChannel,
+ InputChannel toChannel);
private static native String nativeDump();
// Input event injection constants defined in InputDispatcher.h.
@@ -230,7 +240,7 @@ public class InputManager {
}
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
- nativeRegisterInputChannel(inputChannels[0], true);
+ nativeRegisterInputChannel(inputChannels[0], null, true);
inputChannels[0].dispose(); // don't need to retain the Java object reference
return inputChannels[1];
}
@@ -238,13 +248,16 @@ public class InputManager {
/**
* Registers an input channel so that it can be used as an input event target.
* @param inputChannel The input channel to register.
+ * @param inputWindowHandle The handle of the input window associated with the
+ * input channel, or null if none.
*/
- public void registerInputChannel(InputChannel inputChannel) {
+ public void registerInputChannel(InputChannel inputChannel,
+ InputWindowHandle inputWindowHandle) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
- nativeRegisterInputChannel(inputChannel, false);
+ nativeRegisterInputChannel(inputChannel, inputWindowHandle, false);
}
/**
@@ -326,28 +339,78 @@ public class InputManager {
nativeSetInputDispatchMode(enabled, frozen);
}
+ /**
+ * Atomically transfers touch focus from one window to another as identified by
+ * their input channels. It is possible for multiple windows to have
+ * touch focus if they support split touch dispatch
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+ * method only transfers touch focus of the specified window without affecting
+ * other windows that may also have touch focus at the same time.
+ * @param fromChannel The channel of a window that currently has touch focus.
+ * @param toChannel The channel of the window that should receive touch focus in
+ * place of the first.
+ * @return True if the transfer was successful. False if the window with the
+ * specified channel did not actually have touch focus at the time of the request.
+ */
+ public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
+ if (fromChannel == null) {
+ throw new IllegalArgumentException("fromChannel must not be null.");
+ }
+ if (toChannel == null) {
+ throw new IllegalArgumentException("toChannel must not be null.");
+ }
+ return nativeTransferTouchFocus(fromChannel, toChannel);
+ }
+
public void dump(PrintWriter pw) {
String dumpStr = nativeDump();
if (dumpStr != null) {
pw.println(dumpStr);
}
}
-
- private static final class VirtualKeyDefinition {
- public int scanCode;
-
- // configured position data, specified in display coords
- public int centerX;
- public int centerY;
- public int width;
- public int height;
- }
-
- private static final class InputDeviceCalibration {
- public String[] keys;
- public String[] values;
+
+ private static final class PointerIcon {
+ public Bitmap bitmap;
+ public float hotSpotX;
+ public float hotSpotY;
+
+ public static PointerIcon load(Resources resources, int resourceId) {
+ PointerIcon icon = new PointerIcon();
+
+ XmlResourceParser parser = resources.getXml(resourceId);
+ final int bitmapRes;
+ try {
+ XmlUtils.beginDocument(parser, "pointer-icon");
+
+ TypedArray a = resources.obtainAttributes(
+ parser, com.android.internal.R.styleable.PointerIcon);
+ bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
+ icon.hotSpotX = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
+ icon.hotSpotY = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
+ a.recycle();
+ } catch (Exception ex) {
+ Slog.e(TAG, "Exception parsing pointer icon resource.", ex);
+ return null;
+ } finally {
+ parser.close();
+ }
+
+ if (bitmapRes == 0) {
+ Slog.e(TAG, "<pointer-icon> is missing bitmap attribute");
+ return null;
+ }
+
+ Drawable drawable = resources.getDrawable(bitmapRes);
+ if (!(drawable instanceof BitmapDrawable)) {
+ Slog.e(TAG, "<pointer-icon> bitmap attribute must refer to a bitmap drawable");
+ return null;
+ }
+
+ icon.bitmap = ((BitmapDrawable)drawable).getBitmap();
+ return icon;
+ }
}
-
+
/*
* Callbacks from native.
*/
@@ -360,7 +423,7 @@ public class InputManager {
@SuppressWarnings("unused")
public void notifyConfigurationChanged(long whenNanos) {
- mWindowManagerService.sendNewConfiguration();
+ mWindowManagerService.mInputMonitor.notifyConfigurationChanged();
}
@SuppressWarnings("unused")
@@ -369,28 +432,35 @@ public class InputManager {
}
@SuppressWarnings("unused")
- public void notifyInputChannelBroken(InputChannel inputChannel) {
- mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel);
+ public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
+ mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputWindowHandle);
}
@SuppressWarnings("unused")
- public long notifyANR(Object token, InputChannel inputChannel) {
- return mWindowManagerService.mInputMonitor.notifyANR(token, inputChannel);
+ public long notifyANR(InputApplicationHandle inputApplicationHandle,
+ InputWindowHandle inputWindowHandle) {
+ return mWindowManagerService.mInputMonitor.notifyANR(
+ inputApplicationHandle, inputWindowHandle);
}
@SuppressWarnings("unused")
- public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
- int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
+ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
- whenNanos, action, flags, keyCode, scanCode, policyFlags, isScreenOn);
+ event, policyFlags, isScreenOn);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean interceptKeyBeforeDispatching(InputWindowHandle focus,
+ KeyEvent event, int policyFlags) {
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(
+ focus, event, policyFlags);
}
@SuppressWarnings("unused")
- public boolean interceptKeyBeforeDispatching(InputChannel focus, int action,
- int flags, int keyCode, int scanCode, int metaState, int repeatCount,
- int policyFlags) {
- return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
- action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
+ public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
+ KeyEvent event, int policyFlags) {
+ return mWindowManagerService.mInputMonitor.dispatchUnhandledKey(
+ focus, event, policyFlags);
}
@SuppressWarnings("unused")
@@ -419,79 +489,6 @@ public class InputManager {
}
@SuppressWarnings("unused")
- public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
- ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
-
- try {
- FileInputStream fis = new FileInputStream(
- "/sys/board_properties/virtualkeys." + deviceName);
- InputStreamReader isr = new InputStreamReader(fis);
- BufferedReader br = new BufferedReader(isr, 2048);
- String str = br.readLine();
- if (str != null) {
- String[] it = str.split(":");
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
- final int N = it.length-6;
- for (int i=0; i<=N; i+=6) {
- if (!"0x01".equals(it[i])) {
- Slog.w(TAG, "Unknown virtual key type at elem #"
- + i + ": " + it[i] + " for device " + deviceName);
- continue;
- }
- try {
- VirtualKeyDefinition key = new VirtualKeyDefinition();
- key.scanCode = Integer.parseInt(it[i+1]);
- key.centerX = Integer.parseInt(it[i+2]);
- key.centerY = Integer.parseInt(it[i+3]);
- key.width = Integer.parseInt(it[i+4]);
- key.height = Integer.parseInt(it[i+5]);
- if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
- + key.scanCode + ": center=" + key.centerX + ","
- + key.centerY + " size=" + key.width + "x"
- + key.height);
- keys.add(key);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Bad number in virtual key definition at region "
- + i + " in: " + str + " for device " + deviceName, e);
- }
- }
- }
- br.close();
- } catch (FileNotFoundException e) {
- Slog.i(TAG, "No virtual keys found for device " + deviceName + ".");
- } catch (IOException e) {
- Slog.w(TAG, "Error reading virtual keys for device " + deviceName + ".", e);
- }
-
- return keys.toArray(new VirtualKeyDefinition[keys.size()]);
- }
-
- @SuppressWarnings("unused")
- public InputDeviceCalibration getInputDeviceCalibration(String deviceName) {
- // Calibration is specified as a sequence of colon-delimited key value pairs.
- Properties properties = new Properties();
- File calibrationFile = new File(Environment.getRootDirectory(),
- CALIBRATION_DIR_PATH + deviceName + ".idc");
- if (calibrationFile.exists()) {
- try {
- properties.load(new FileInputStream(calibrationFile));
- } catch (IOException ex) {
- Slog.w(TAG, "Error reading input device calibration properties for device "
- + deviceName + " from " + calibrationFile + ".", ex);
- }
- } else {
- Slog.i(TAG, "No input device calibration properties found for device "
- + deviceName + ".");
- return null;
- }
-
- InputDeviceCalibration calibration = new InputDeviceCalibration();
- calibration.keys = properties.keySet().toArray(new String[properties.size()]);
- calibration.values = properties.values().toArray(new String[properties.size()]);
- return calibration;
- }
-
- @SuppressWarnings("unused")
public String[] getExcludedDeviceNames() {
ArrayList<String> names = new ArrayList<String>();
@@ -539,5 +536,19 @@ public class InputManager {
}
return result;
}
+
+ @SuppressWarnings("unused")
+ public int getPointerLayer() {
+ return mWindowManagerService.mPolicy.windowTypeToLayerLw(
+ WindowManager.LayoutParams.TYPE_POINTER)
+ * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ + WindowManagerService.TYPE_LAYER_OFFSET;
+ }
+
+ @SuppressWarnings("unused")
+ public PointerIcon getPointerIcon() {
+ return PointerIcon.load(mContext.getResources(),
+ com.android.internal.R.drawable.pointer_arrow_icon);
+ }
}
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index ecad3cc..21c1e81 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -61,18 +61,21 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Settings.Secure;
+import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.EventLog;
+import android.util.Pair;
import android.util.Slog;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.view.IWindowManager;
import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -80,6 +83,7 @@ import java.io.PrintWriter;
import java.text.Collator;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -93,6 +97,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final String TAG = "InputManagerService";
static final int MSG_SHOW_IM_PICKER = 1;
+ static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
+ static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3;
+ static final int MSG_SHOW_IM_CONFIG = 4;
static final int MSG_UNBIND_INPUT = 1000;
static final int MSG_BIND_INPUT = 1010;
@@ -109,8 +116,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final long TIME_TO_RECONNECT = 10*1000;
+ private static final int NOT_A_SUBTYPE_ID = -1;
+ private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
+ private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
+ private static final String SUBTYPE_MODE_VOICE = "voice";
+
+ // TODO: Will formalize this value as API
+ private static final String SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME =
+ "excludeFromLastInputMethod";
+
final Context mContext;
+ final Resources mRes;
final Handler mHandler;
+ final InputMethodSettings mSettings;
final SettingsObserver mSettingsObserver;
final StatusBarManagerService mStatusBar;
final IWindowManager mIWindowManager;
@@ -120,13 +138,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// All known input methods. mMethodMap also serves as the global
// lock for this class.
- final ArrayList<InputMethodInfo> mMethodList
- = new ArrayList<InputMethodInfo>();
- final HashMap<String, InputMethodInfo> mMethodMap
- = new HashMap<String, InputMethodInfo>();
-
- final TextUtils.SimpleStringSplitter mStringColonSplitter
- = new TextUtils.SimpleStringSplitter(':');
+ final ArrayList<InputMethodInfo> mMethodList = new ArrayList<InputMethodInfo>();
+ final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<String, InputMethodInfo>();
class SessionState {
final ClientState client;
@@ -224,6 +237,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
String mCurId;
/**
+ * The current subtype of the current input method.
+ */
+ private InputMethodSubtype mCurrentSubtype;
+
+ // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
+ private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
+ mShortcutInputMethodsAndSubtypes =
+ new HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>();
+
+ /**
* Set to true if our ServiceConnection is currently actively bound to
* a service (whether or not we have gotten its IBinder back yet).
*/
@@ -292,6 +315,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
AlertDialog mSwitchingDialog;
InputMethodInfo[] mIms;
CharSequence[] mItems;
+ int[] mSubtypeIds;
class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
@@ -299,6 +323,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
}
@Override public void onChange(boolean selfChange) {
@@ -351,9 +377,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!doit) {
return true;
}
-
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD, "");
+ resetSelectedInputMethodAndSubtypeLocked("");
chooseNewDefaultIMELocked();
return true;
}
@@ -406,19 +430,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// Uh oh, current input method is no longer around!
// Pick another one...
Slog.i(TAG, "Current input method removed: " + curInputMethodId);
+ mStatusBar.setIMEButtonVisible(mCurToken, false);
if (!chooseNewDefaultIMELocked()) {
changed = true;
curIm = null;
- curInputMethodId = "";
Slog.i(TAG, "Unsetting current input method");
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD,
- curInputMethodId);
+ resetSelectedInputMethodAndSubtypeLocked("");
}
}
}
}
-
+
if (curIm == null) {
// We currently don't have a default input method... is
// one now available?
@@ -449,6 +471,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public InputMethodManagerService(Context context, StatusBarManagerService statusBar) {
mContext = context;
+ mRes = context.getResources();
mHandler = new Handler(this);
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
@@ -469,27 +492,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mStatusBar = statusBar;
statusBar.setIconVisibility("ime", false);
+ // mSettings should be created before buildInputMethodListLocked
+ mSettings = new InputMethodSettings(
+ mRes, context.getContentResolver(), mMethodMap, mMethodList);
buildInputMethodListLocked(mMethodList, mMethodMap);
+ mSettings.enableAllIMEsIfThereIsNoEnabledIME();
- final String enabledStr = Settings.Secure.getString(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_INPUT_METHODS);
- Slog.i(TAG, "Enabled input methods: " + enabledStr);
- final String defaultIme = Settings.Secure.getString(mContext
- .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
- if (enabledStr == null || TextUtils.isEmpty(defaultIme)) {
- Slog.i(TAG, "Enabled input methods or default IME has not been set, enabling all");
+ if (TextUtils.isEmpty(Settings.Secure.getString(
+ mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD))) {
InputMethodInfo defIm = null;
- StringBuilder sb = new StringBuilder(256);
- final int N = mMethodList.size();
- for (int i=0; i<N; i++) {
- InputMethodInfo imi = mMethodList.get(i);
- Slog.i(TAG, "Adding: " + imi.getId());
- if (i > 0) sb.append(':');
- sb.append(imi.getId());
+ for (InputMethodInfo imi: mMethodList) {
if (defIm == null && imi.getIsDefaultResourceId() != 0) {
try {
- Resources res = mContext.createPackageContext(
+ Resources res = context.createPackageContext(
imi.getPackageName(), 0).getResources();
if (res.getBoolean(imi.getIsDefaultResourceId())) {
defIm = imi;
@@ -500,15 +515,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
}
- if (defIm == null && N > 0) {
+ if (defIm == null && mMethodList.size() > 0) {
defIm = mMethodList.get(0);
Slog.i(TAG, "No default found, using " + defIm.getId());
}
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.ENABLED_INPUT_METHODS, sb.toString());
if (defIm != null) {
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId());
+ setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
}
}
@@ -552,29 +564,40 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public List<InputMethodInfo> getEnabledInputMethodList() {
synchronized (mMethodMap) {
- return getEnabledInputMethodListLocked();
+ return mSettings.getEnabledInputMethodListLocked();
}
}
- List<InputMethodInfo> getEnabledInputMethodListLocked() {
- final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
-
- final String enabledStr = Settings.Secure.getString(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_INPUT_METHODS);
- if (enabledStr != null) {
- final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
- splitter.setString(enabledStr);
+ private HashMap<InputMethodInfo, List<InputMethodSubtype>>
+ getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked() {
+ HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes =
+ new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
+ for (InputMethodInfo imi: getEnabledInputMethodList()) {
+ enabledInputMethodAndSubtypes.put(
+ imi, getEnabledInputMethodSubtypeListLocked(imi, true));
+ }
+ return enabledInputMethodAndSubtypes;
+ }
- while (splitter.hasNext()) {
- InputMethodInfo info = mMethodMap.get(splitter.next());
- if (info != null) {
- res.add(info);
- }
- }
+ public List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(InputMethodInfo imi,
+ boolean allowsImplicitlySelectedSubtypes) {
+ if (imi == null && mCurMethodId != null) {
+ imi = mMethodMap.get(mCurMethodId);
+ }
+ final List<InputMethodSubtype> enabledSubtypes =
+ mSettings.getEnabledInputMethodSubtypeListLocked(imi);
+ if (!allowsImplicitlySelectedSubtypes || enabledSubtypes.size() > 0) {
+ return enabledSubtypes;
+ } else {
+ return getApplicableSubtypesLocked(mRes, getSubtypes(imi));
}
+ }
- return res;
+ public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
+ boolean allowsImplicitlySelectedSubtypes) {
+ synchronized (mMethodMap) {
+ return getEnabledInputMethodSubtypeListLocked(imi, allowsImplicitlySelectedSubtypes);
+ }
}
public void addClient(IInputMethodClient client,
@@ -959,21 +982,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ public void setIMEButtonVisible(IBinder token, boolean visible) {
+ int uid = Binder.getCallingUid();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (token == null || mCurToken != token) {
+ Slog.w(TAG, "Ignoring setIMEButtonVisible of uid " + uid + " token: " + token);
+ return;
+ }
+
+ synchronized (mMethodMap) {
+ mStatusBar.setIMEButtonVisible(token, visible);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
void updateFromSettingsLocked() {
// We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
// ENABLED_INPUT_METHODS is taking care of keeping them correctly in
// sync, so we will never have a DEFAULT_INPUT_METHOD that is not
// enabled.
String id = Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD);
- if (id != null && id.length() > 0) {
+ Settings.Secure.DEFAULT_INPUT_METHOD);
+ // There is no input method selected, try to choose new applicable input method.
+ if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) {
+ id = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD);
+ }
+ if (!TextUtils.isEmpty(id)) {
try {
- setInputMethodLocked(id);
+ setInputMethodLocked(id, getSelectedInputMethodSubtypeId(id));
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unknown input method from prefs: " + id, e);
mCurMethodId = null;
unbindCurrentMethodLocked(true);
}
+ mShortcutInputMethodsAndSubtypes.clear();
} else {
// There is no longer an input method set, so stop any current one.
mCurMethodId = null;
@@ -981,21 +1027,54 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- void setInputMethodLocked(String id) {
+ /* package */ void setInputMethodLocked(String id, int subtypeId) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + id);
}
if (id.equals(mCurMethodId)) {
+ InputMethodSubtype subtype = null;
+ if (subtypeId >= 0 && subtypeId < info.getSubtypeCount()) {
+ subtype = info.getSubtypeAt(subtypeId);
+ }
+ if (subtype != mCurrentSubtype) {
+ synchronized (mMethodMap) {
+ if (subtype != null) {
+ setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
+ }
+ if (mCurMethod != null) {
+ try {
+ if (mInputShown) {
+ // If mInputShown is false, there is no IME button on the
+ // system bar.
+ // Thus there is no need to make it invisible explicitly.
+ mStatusBar.setIMEButtonVisible(mCurToken, true);
+ }
+ // If subtype is null, try to find the most applicable one from
+ // getCurrentInputMethodSubtype.
+ if (subtype == null) {
+ subtype = getCurrentInputMethodSubtype();
+ }
+ mCurMethod.changeInputMethodSubtype(subtype);
+ } catch (RemoteException e) {
+ return;
+ }
+ }
+ }
+ }
return;
}
final long ident = Binder.clearCallingIdentity();
try {
+ // Set a subtype to this input method.
+ // subtypeId the name of a subtype which will be set.
+ setSelectedInputMethodAndSubtypeLocked(info, subtypeId, false);
+ // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
+ // because mCurMethodId is stored as a history in
+ // setSelectedInputMethodAndSubtypeLocked().
mCurMethodId = id;
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD, id);
if (ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -1089,9 +1168,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid "
+ uid + ": " + client);
+ mStatusBar.setIMEButtonVisible(mCurToken, false);
return false;
}
} catch (RemoteException e) {
+ mStatusBar.setIMEButtonVisible(mCurToken, false);
return false;
}
}
@@ -1164,11 +1245,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
mCurFocusedWindow = windowToken;
+ // Should we auto-show the IME even if the caller has not
+ // specified what should be done with it?
+ // We only do this automatically if the window can resize
+ // to accommodate the IME (so what the user sees will give
+ // them good context without input information being obscured
+ // by the IME) or if running on a large screen where there
+ // is more room for the target window + IME.
+ final boolean doAutoShow =
+ (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
+ == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+ || mRes.getConfiguration().isLayoutSizeAtLeast(
+ Configuration.SCREENLAYOUT_SIZE_LARGE);
+
switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
- if (!isTextEditor || (softInputMode &
- WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
- != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
+ if (!isTextEditor || !doAutoShow) {
if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) {
// There is no focus view, and this window will
// be behind any soft input window, so hide the
@@ -1176,13 +1268,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
}
- } else if (isTextEditor && (softInputMode &
- WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
- == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
- && (softInputMode &
- WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
+ } else if (isTextEditor && doAutoShow && (softInputMode &
+ WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
// There is a focus view, and we are navigating forward
// into the window, so show the input window for the user.
+ // We only do this automatically if the window an resize
+ // to accomodate the IME (so what the user sees will give
+ // them good context without input information being obscured
+ // by the IME) or if running on a large screen where there
+ // is more room for the target window + IME.
if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
}
@@ -1223,15 +1317,59 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (mMethodMap) {
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
- Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of uid "
+ Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
+ Binder.getCallingUid() + ": " + client);
}
- mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER);
+ // Always call subtype picker, because subtype picker is a superset of input method
+ // picker.
+ mHandler.sendEmptyMessage(MSG_SHOW_IM_SUBTYPE_PICKER);
}
}
public void setInputMethod(IBinder token, String id) {
+ setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
+ }
+
+ public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
+ synchronized (mMethodMap) {
+ if (subtype != null) {
+ setInputMethodWithSubtypeId(token, id, getSubtypeIdFromHashCode(
+ mMethodMap.get(id), subtype.hashCode()));
+ } else {
+ setInputMethod(token, id);
+ }
+ }
+ }
+
+ public void showInputMethodAndSubtypeEnablerFromClient(
+ IInputMethodClient client, String inputMethodId) {
+ synchronized (mMethodMap) {
+ if (mCurClient == null || client == null
+ || mCurClient.client.asBinder() != client.asBinder()) {
+ Slog.w(TAG, "Ignoring showInputMethodAndSubtypeEnablerFromClient of: " + client);
+ }
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
+ MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
+ }
+ }
+
+ public boolean switchToLastInputMethod(IBinder token) {
+ synchronized (mMethodMap) {
+ Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
+ if (lastIme != null) {
+ InputMethodInfo imi = mMethodMap.get(lastIme.first);
+ if (imi != null) {
+ setInputMethodWithSubtypeId(token, lastIme.first, getSubtypeIdFromHashCode(
+ imi, Integer.valueOf(lastIme.second)));
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
synchronized (mMethodMap) {
if (token == null) {
if (mContext.checkCallingOrSelfPermission(
@@ -1249,7 +1387,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
long ident = Binder.clearCallingIdentity();
try {
- setInputMethodLocked(id);
+ setInputMethodLocked(id, subtypeId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1315,6 +1453,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
showInputMethodMenu();
return true;
+ case MSG_SHOW_IM_SUBTYPE_PICKER:
+ showInputMethodSubtypeMenu();
+ return true;
+
+ case MSG_SHOW_IM_SUBTYPE_ENABLER:
+ args = (HandlerCaller.SomeArgs)msg.obj;
+ showInputMethodAndSubtypeEnabler((String)args.arg1);
+ return true;
+
+ case MSG_SHOW_IM_CONFIG:
+ showConfigureInputMethods();
+ return true;
+
// ---------------------------------------------------------
case MSG_UNBIND_INPUT:
@@ -1413,8 +1564,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
& ApplicationInfo.FLAG_SYSTEM) != 0;
}
+ private static ArrayList<InputMethodSubtype> getSubtypes(InputMethodInfo imi) {
+ ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ subtypes.add(imi.getSubtypeAt(i));
+ }
+ return subtypes;
+ }
+
private boolean chooseNewDefaultIMELocked() {
- List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
+ List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
if (enabled != null && enabled.size() > 0) {
// We'd prefer to fall back on a system IME, since that is safer.
int i=enabled.size();
@@ -1425,9 +1585,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
break;
}
}
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD,
- enabled.get(i).getId());
+ InputMethodInfo imi = enabled.get(i);
+ if (DEBUG) {
+ Slog.d(TAG, "New default IME was selected: " + imi.getId());
+ }
+ resetSelectedInputMethodAndSubtypeLocked(imi.getId());
return true;
}
@@ -1440,7 +1602,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
map.clear();
PackageManager pm = mContext.getPackageManager();
- final Configuration config = mContext.getResources().getConfiguration();
+ final Configuration config = mRes.getConfiguration();
final boolean haveHardKeyboard = config.keyboard == Configuration.KEYBOARD_QWERTY;
String disabledSysImes = Settings.Secure.getString(mContext.getContentResolver(),
Secure.DISABLED_SYSTEM_INPUT_METHODS);
@@ -1498,7 +1660,34 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// ----------------------------------------------------------------------
- void showInputMethodMenu() {
+ private void showInputMethodMenu() {
+ showInputMethodMenuInternal(false);
+ }
+
+ private void showInputMethodSubtypeMenu() {
+ showInputMethodMenuInternal(true);
+ }
+
+ private void showInputMethodAndSubtypeEnabler(String inputMethodId) {
+ Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if (!TextUtils.isEmpty(inputMethodId)) {
+ intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId);
+ }
+ mContext.startActivity(intent);
+ }
+
+ private void showConfigureInputMethods() {
+ Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mContext.startActivity(intent);
+ }
+
+ private void showInputMethodMenuInternal(boolean showSubtypes) {
if (DEBUG) Slog.v(TAG, "Show switching menu");
final Context context = mContext;
@@ -1507,48 +1696,81 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
String lastInputMethodId = Settings.Secure.getString(context
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+ int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
- final List<InputMethodInfo> immis = getEnabledInputMethodList();
-
- if (immis == null) {
- return;
- }
-
synchronized (mMethodMap) {
- hideInputMethodMenuLocked();
+ final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
+ getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked();
+ if (immis == null || immis.size() == 0) {
+ return;
+ }
- int N = immis.size();
+ hideInputMethodMenuLocked();
- final Map<CharSequence, InputMethodInfo> imMap =
- new TreeMap<CharSequence, InputMethodInfo>(Collator.getInstance());
+ final Map<CharSequence, Pair<InputMethodInfo, Integer>> imMap =
+ new TreeMap<CharSequence, Pair<InputMethodInfo, Integer>>(Collator.getInstance());
- for (int i = 0; i < N; ++i) {
- InputMethodInfo property = immis.get(i);
- if (property == null) {
- continue;
+ for (InputMethodInfo imi: immis.keySet()) {
+ if (imi == null) continue;
+ List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
+ HashSet<String> enabledSubtypeSet = new HashSet<String>();
+ for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) {
+ enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
+ }
+ ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi);
+ CharSequence label = imi.loadLabel(pm);
+ if (showSubtypes && enabledSubtypeSet.size() > 0) {
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int j = 0; j < subtypeCount; ++j) {
+ InputMethodSubtype subtype = imi.getSubtypeAt(j);
+ if (enabledSubtypeSet.contains(String.valueOf(subtype.hashCode()))) {
+ CharSequence title;
+ int nameResId = subtype.getNameResId();
+ String mode = subtype.getMode();
+ if (nameResId != 0) {
+ title = pm.getText(imi.getPackageName(), nameResId,
+ imi.getServiceInfo().applicationInfo);
+ } else {
+ CharSequence language = subtype.getLocale();
+ // TODO: Use more friendly Title and UI
+ title = label + "," + (mode == null ? "" : mode) + ","
+ + (language == null ? "" : language);
+ }
+ imMap.put(title, new Pair<InputMethodInfo, Integer>(imi, j));
+ }
+ }
+ } else {
+ imMap.put(label,
+ new Pair<InputMethodInfo, Integer>(imi, NOT_A_SUBTYPE_ID));
}
- imMap.put(property.loadLabel(pm), property);
}
- N = imMap.size();
+ final int N = imMap.size();
mItems = imMap.keySet().toArray(new CharSequence[N]);
- mIms = imMap.values().toArray(new InputMethodInfo[N]);
-
+ mIms = new InputMethodInfo[N];
+ mSubtypeIds = new int[N];
int checkedItem = 0;
for (int i = 0; i < N; ++i) {
+ Pair<InputMethodInfo, Integer> value = imMap.get(mItems[i]);
+ mIms[i] = value.first;
+ mSubtypeIds[i] = value.second;
if (mIms[i].getId().equals(lastInputMethodId)) {
- checkedItem = i;
- break;
+ int subtypeId = mSubtypeIds[i];
+ if ((subtypeId == NOT_A_SUBTYPE_ID)
+ || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
+ || (subtypeId == lastInputMethodSubtypeId)) {
+ checkedItem = i;
+ }
}
}
-
+
AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
hideInputMethodMenu();
}
};
-
+
TypedArray a = context.obtainStyledAttributes(null,
com.android.internal.R.styleable.DialogPreference,
com.android.internal.R.attr.alertDialogStyle, 0);
@@ -1562,23 +1784,38 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
.setIcon(a.getDrawable(
com.android.internal.R.styleable.DialogPreference_dialogTitle));
a.recycle();
-
+
mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
synchronized (mMethodMap) {
- if (mIms == null || mIms.length <= which) {
+ if (mIms == null || mIms.length <= which
+ || mSubtypeIds == null || mSubtypeIds.length <= which) {
return;
}
InputMethodInfo im = mIms[which];
+ int subtypeId = mSubtypeIds[which];
hideInputMethodMenu();
if (im != null) {
- setInputMethodLocked(im.getId());
+ if ((subtypeId < 0)
+ || (subtypeId >= im.getSubtypeCount())) {
+ subtypeId = NOT_A_SUBTYPE_ID;
+ }
+ setInputMethodLocked(im.getId(), subtypeId);
}
}
}
});
+ if (showSubtypes) {
+ mDialogBuilder.setPositiveButton(
+ com.android.internal.R.string.configure_input_methods,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ showConfigureInputMethods();
+ }
+ });
+ }
mSwitchingDialog = mDialogBuilder.create();
mSwitchingDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
@@ -1630,80 +1867,795 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// Make sure this is a valid input method.
InputMethodInfo imm = mMethodMap.get(id);
if (imm == null) {
- if (imm == null) {
- throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
+ throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
+ }
+
+ List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
+ .getEnabledInputMethodsAndSubtypeListLocked();
+
+ if (enabled) {
+ for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) {
+ if (pair.first.equals(id)) {
+ // We are enabling this input method, but it is already enabled.
+ // Nothing to do. The previous state was enabled.
+ return true;
+ }
+ }
+ mSettings.appendAndPutEnabledInputMethodLocked(id, false);
+ // Previous state was disabled.
+ return false;
+ } else {
+ StringBuilder builder = new StringBuilder();
+ if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
+ builder, enabledInputMethodsList, id)) {
+ // Disabled input method is currently selected, switch to another one.
+ String selId = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD);
+ if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
+ Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
+ resetSelectedInputMethodAndSubtypeLocked("");
+ }
+ // Previous state was enabled.
+ return true;
+ } else {
+ // We are disabling the input method but it is already disabled.
+ // Nothing to do. The previous state was disabled.
+ return false;
}
}
+ }
- StringBuilder builder = new StringBuilder(256);
+ private boolean canAddToLastInputMethod(InputMethodSubtype subtype) {
+ if (subtype == null) return true;
+ String[] extraValues = subtype.getExtraValue().split(",");
+ final int N = extraValues.length;
+ for (int i = 0; i < N; ++i) {
+ if (SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME.equals(extraValues[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
- boolean removed = false;
- String firstId = null;
+ private void saveCurrentInputMethodAndSubtypeToHistory() {
+ String subtypeId = NOT_A_SUBTYPE_ID_STR;
+ if (mCurrentSubtype != null) {
+ subtypeId = String.valueOf(mCurrentSubtype.hashCode());
+ }
+ if (canAddToLastInputMethod(mCurrentSubtype)) {
+ mSettings.addSubtypeToHistory(mCurMethodId, subtypeId);
+ }
+ }
- // Look through the currently enabled input methods.
- String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.ENABLED_INPUT_METHODS);
- if (enabledStr != null) {
- final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
- splitter.setString(enabledStr);
- while (splitter.hasNext()) {
- String curId = splitter.next();
- if (curId.equals(id)) {
- if (enabled) {
- // We are enabling this input method, but it is
- // already enabled. Nothing to do. The previous
- // state was enabled.
- return true;
+ private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
+ boolean setSubtypeOnly) {
+ // Update the history of InputMethod and Subtype
+ saveCurrentInputMethodAndSubtypeToHistory();
+
+ // Set Subtype here
+ if (imi == null || subtypeId < 0) {
+ mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+ mCurrentSubtype = null;
+ } else {
+ if (subtypeId < imi.getSubtypeCount()) {
+ InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId);
+ mSettings.putSelectedSubtype(subtype.hashCode());
+ mCurrentSubtype = subtype;
+ } else {
+ mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+ mCurrentSubtype = null;
+ }
+ }
+
+ if (!setSubtypeOnly) {
+ // Set InputMethod here
+ mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "");
+ }
+ }
+
+ private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
+ InputMethodInfo imi = mMethodMap.get(newDefaultIme);
+ int lastSubtypeId = NOT_A_SUBTYPE_ID;
+ // newDefaultIme is empty when there is no candidate for the selected IME.
+ if (imi != null && !TextUtils.isEmpty(newDefaultIme)) {
+ String subtypeHashCode = mSettings.getLastSubtypeForInputMethodLocked(newDefaultIme);
+ if (subtypeHashCode != null) {
+ try {
+ lastSubtypeId = getSubtypeIdFromHashCode(
+ imi, Integer.valueOf(subtypeHashCode));
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e);
+ }
+ }
+ }
+ setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false);
+ }
+
+ private int getSelectedInputMethodSubtypeId(String id) {
+ InputMethodInfo imi = mMethodMap.get(id);
+ if (imi == null) {
+ return NOT_A_SUBTYPE_ID;
+ }
+ int subtypeId;
+ try {
+ subtypeId = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE);
+ } catch (SettingNotFoundException e) {
+ return NOT_A_SUBTYPE_ID;
+ }
+ return getSubtypeIdFromHashCode(imi, subtypeId);
+ }
+
+ private int getSubtypeIdFromHashCode(InputMethodInfo imi, int subtypeHashCode) {
+ if (imi != null) {
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ InputMethodSubtype ims = imi.getSubtypeAt(i);
+ if (subtypeHashCode == ims.hashCode()) {
+ return i;
+ }
+ }
+ }
+ return NOT_A_SUBTYPE_ID;
+ }
+
+ private static ArrayList<InputMethodSubtype> getApplicableSubtypesLocked(
+ Resources res, List<InputMethodSubtype> subtypes) {
+ final String systemLocale = res.getConfiguration().locale.toString();
+ if (TextUtils.isEmpty(systemLocale)) return new ArrayList<InputMethodSubtype>();
+ HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap =
+ new HashMap<String, InputMethodSubtype>();
+ final int N = subtypes.size();
+ boolean containsKeyboardSubtype = false;
+ for (int i = 0; i < N; ++i) {
+ InputMethodSubtype subtype = subtypes.get(i);
+ final String locale = subtype.getLocale();
+ final String mode = subtype.getMode();
+ // When system locale starts with subtype's locale, that subtype will be applicable
+ // for system locale
+ // For instance, it's clearly applicable for cases like system locale = en_US and
+ // subtype = en, but it is not necessarily considered applicable for cases like system
+ // locale = en and subtype = en_US.
+ // We just call systemLocale.startsWith(locale) in this function because there is no
+ // need to find applicable subtypes aggressively unlike
+ // findLastResortApplicableSubtypeLocked.
+ if (systemLocale.startsWith(locale)) {
+ InputMethodSubtype applicableSubtype = applicableModeAndSubtypesMap.get(mode);
+ // If more applicable subtypes are contained, skip.
+ if (applicableSubtype != null
+ && systemLocale.equals(applicableSubtype.getLocale())) continue;
+ applicableModeAndSubtypesMap.put(mode, subtype);
+ if (!containsKeyboardSubtype
+ && SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
+ containsKeyboardSubtype = true;
+ }
+ }
+ }
+ ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
+ applicableModeAndSubtypesMap.values());
+ if (!containsKeyboardSubtype) {
+ InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked(
+ res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true);
+ if (lastResortKeyboardSubtype != null) {
+ applicableSubtypes.add(lastResortKeyboardSubtype);
+ }
+ }
+ return applicableSubtypes;
+ }
+
+ /**
+ * If there are no selected subtypes, tries finding the most applicable one according to the
+ * given locale.
+ * @param subtypes this function will search the most applicable subtype in subtypes
+ * @param mode subtypes will be filtered by mode
+ * @param locale subtypes will be filtered by locale
+ * @param canIgnoreLocaleAsLastResort if this function can't find the most applicable subtype,
+ * it will return the first subtype matched with mode
+ * @return the most applicable subtypeId
+ */
+ private static InputMethodSubtype findLastResortApplicableSubtypeLocked(
+ Resources res, List<InputMethodSubtype> subtypes, String mode, String locale,
+ boolean canIgnoreLocaleAsLastResort) {
+ if (subtypes == null || subtypes.size() == 0) {
+ return null;
+ }
+ if (TextUtils.isEmpty(locale)) {
+ locale = res.getConfiguration().locale.toString();
+ }
+ final String language = locale.substring(0, 2);
+ boolean partialMatchFound = false;
+ InputMethodSubtype applicableSubtype = null;
+ InputMethodSubtype firstMatchedModeSubtype = null;
+ final int N = subtypes.size();
+ for (int i = 0; i < N; ++i) {
+ InputMethodSubtype subtype = subtypes.get(i);
+ final String subtypeLocale = subtype.getLocale();
+ // An applicable subtype should match "mode". If mode is null, mode will be ignored,
+ // and all subtypes with all modes can be candidates.
+ if (mode == null || subtypes.get(i).getMode().equalsIgnoreCase(mode)) {
+ if (firstMatchedModeSubtype == null) {
+ firstMatchedModeSubtype = subtype;
+ }
+ if (locale.equals(subtypeLocale)) {
+ // Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
+ applicableSubtype = subtype;
+ break;
+ } else if (!partialMatchFound && subtypeLocale.startsWith(language)) {
+ // Partial match (e.g. system locale is "en_US" and subtype locale is "en")
+ applicableSubtype = subtype;
+ partialMatchFound = true;
+ }
+ }
+ }
+
+ if (applicableSubtype == null && canIgnoreLocaleAsLastResort) {
+ return firstMatchedModeSubtype;
+ }
+
+ // The first subtype applicable to the system locale will be defined as the most applicable
+ // subtype.
+ if (DEBUG) {
+ if (applicableSubtype != null) {
+ Slog.d(TAG, "Applicable InputMethodSubtype was found: "
+ + applicableSubtype.getMode() + "," + applicableSubtype.getLocale());
+ }
+ }
+ return applicableSubtype;
+ }
+
+ // If there are no selected shortcuts, tries finding the most applicable ones.
+ private Pair<InputMethodInfo, InputMethodSubtype>
+ findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
+ List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
+ InputMethodInfo mostApplicableIMI = null;
+ InputMethodSubtype mostApplicableSubtype = null;
+ boolean foundInSystemIME = false;
+
+ // Search applicable subtype for each InputMethodInfo
+ for (InputMethodInfo imi: imis) {
+ final String imiId = imi.getId();
+ if (foundInSystemIME && !imiId.equals(mCurMethodId)) {
+ continue;
+ }
+ InputMethodSubtype subtype = null;
+ final List<InputMethodSubtype> enabledSubtypes =
+ getEnabledInputMethodSubtypeList(imi, true);
+ // 1. Search by the current subtype's locale from enabledSubtypes.
+ if (mCurrentSubtype != null) {
+ subtype = findLastResortApplicableSubtypeLocked(
+ mRes, enabledSubtypes, mode, mCurrentSubtype.getLocale(), false);
+ }
+ // 2. Search by the system locale from enabledSubtypes.
+ // 3. Search the first enabled subtype matched with mode from enabledSubtypes.
+ if (subtype == null) {
+ subtype = findLastResortApplicableSubtypeLocked(
+ mRes, enabledSubtypes, mode, null, true);
+ }
+ // 4. Search by the current subtype's locale from all subtypes.
+ if (subtype == null && mCurrentSubtype != null) {
+ subtype = findLastResortApplicableSubtypeLocked(
+ mRes, getSubtypes(imi), mode, mCurrentSubtype.getLocale(), false);
+ }
+ // 5. Search by the system locale from all subtypes.
+ // 6. Search the first enabled subtype matched with mode from all subtypes.
+ if (subtype == null) {
+ subtype = findLastResortApplicableSubtypeLocked(
+ mRes, getSubtypes(imi), mode, null, true);
+ }
+ if (subtype != null) {
+ if (imiId.equals(mCurMethodId)) {
+ // The current input method is the most applicable IME.
+ mostApplicableIMI = imi;
+ mostApplicableSubtype = subtype;
+ break;
+ } else if (!foundInSystemIME) {
+ // The system input method is 2nd applicable IME.
+ mostApplicableIMI = imi;
+ mostApplicableSubtype = subtype;
+ if ((imi.getServiceInfo().applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ foundInSystemIME = true;
+ }
+ }
+ }
+ }
+ if (DEBUG) {
+ if (mostApplicableIMI != null) {
+ Slog.w(TAG, "Most applicable shortcut input method was:"
+ + mostApplicableIMI.getId());
+ if (mostApplicableSubtype != null) {
+ Slog.w(TAG, "Most applicable shortcut input method subtype was:"
+ + "," + mostApplicableSubtype.getMode() + ","
+ + mostApplicableSubtype.getLocale());
+ }
+ }
+ }
+ if (mostApplicableIMI != null) {
+ return new Pair<InputMethodInfo, InputMethodSubtype> (mostApplicableIMI,
+ mostApplicableSubtype);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @return Return the current subtype of this input method.
+ */
+ public InputMethodSubtype getCurrentInputMethodSubtype() {
+ boolean subtypeIsSelected = false;
+ try {
+ subtypeIsSelected = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE) != NOT_A_SUBTYPE_ID;
+ } catch (SettingNotFoundException e) {
+ }
+ synchronized (mMethodMap) {
+ if (!subtypeIsSelected || mCurrentSubtype == null) {
+ String lastInputMethodId = Settings.Secure.getString(
+ mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+ int subtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
+ if (subtypeId == NOT_A_SUBTYPE_ID) {
+ InputMethodInfo imi = mMethodMap.get(lastInputMethodId);
+ if (imi != null) {
+ // If there are no selected subtypes, the framework will try to find
+ // the most applicable subtype from explicitly or implicitly enabled
+ // subtypes.
+ List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
+ getEnabledInputMethodSubtypeList(imi, true);
+ // If there is only one explicitly or implicitly enabled subtype,
+ // just returns it.
+ if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
+ mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
+ } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
+ mCurrentSubtype = findLastResortApplicableSubtypeLocked(
+ mRes, explicitlyOrImplicitlyEnabledSubtypes,
+ SUBTYPE_MODE_KEYBOARD, null, true);
+ if (mCurrentSubtype == null) {
+ mCurrentSubtype = findLastResortApplicableSubtypeLocked(
+ mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null,
+ true);
+ }
+ }
+ }
+ } else {
+ mCurrentSubtype =
+ getSubtypes(mMethodMap.get(lastInputMethodId)).get(subtypeId);
+ }
+ }
+ return mCurrentSubtype;
+ }
+ }
+
+ private void addShortcutInputMethodAndSubtypes(InputMethodInfo imi,
+ InputMethodSubtype subtype) {
+ if (mShortcutInputMethodsAndSubtypes.containsKey(imi)) {
+ mShortcutInputMethodsAndSubtypes.get(imi).add(subtype);
+ } else {
+ ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes.add(subtype);
+ mShortcutInputMethodsAndSubtypes.put(imi, subtypes);
+ }
+ }
+
+ // TODO: We should change the return type from List to List<Parcelable>
+ public List getShortcutInputMethodsAndSubtypes() {
+ synchronized (mMethodMap) {
+ ArrayList<Object> ret = new ArrayList<Object>();
+ if (mShortcutInputMethodsAndSubtypes.size() == 0) {
+ // If there are no selected shortcut subtypes, the framework will try to find
+ // the most applicable subtype from all subtypes whose mode is
+ // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
+ Pair<InputMethodInfo, InputMethodSubtype> info =
+ findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
+ SUBTYPE_MODE_VOICE);
+ if (info != null) {
+ ret.add(info.first);
+ ret.add(info.second);
+ }
+ return ret;
+ }
+ for (InputMethodInfo imi: mShortcutInputMethodsAndSubtypes.keySet()) {
+ ret.add(imi);
+ for (InputMethodSubtype subtype: mShortcutInputMethodsAndSubtypes.get(imi)) {
+ ret.add(subtype);
+ }
+ }
+ return ret;
+ }
+ }
+
+ public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
+ synchronized (mMethodMap) {
+ if (subtype != null && mCurMethodId != null) {
+ InputMethodInfo imi = mMethodMap.get(mCurMethodId);
+ int subtypeId = getSubtypeIdFromHashCode(imi, subtype.hashCode());
+ if (subtypeId != NOT_A_SUBTYPE_ID) {
+ setInputMethodLocked(mCurMethodId, subtypeId);
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Utility class for putting and getting settings for InputMethod
+ * TODO: Move all putters and getters of settings to this class.
+ */
+ private static class InputMethodSettings {
+ // The string for enabled input method is saved as follows:
+ // example: ("ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0")
+ private static final char INPUT_METHOD_SEPARATER = ':';
+ private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
+ private final TextUtils.SimpleStringSplitter mInputMethodSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
+
+ private final TextUtils.SimpleStringSplitter mSubtypeSplitter =
+ new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
+
+ private final Resources mRes;
+ private final ContentResolver mResolver;
+ private final HashMap<String, InputMethodInfo> mMethodMap;
+ private final ArrayList<InputMethodInfo> mMethodList;
+
+ private String mEnabledInputMethodsStrCache;
+
+ private static void buildEnabledInputMethodsSettingString(
+ StringBuilder builder, Pair<String, ArrayList<String>> pair) {
+ String id = pair.first;
+ ArrayList<String> subtypes = pair.second;
+ builder.append(id);
+ // Inputmethod and subtypes are saved in the settings as follows:
+ // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
+ for (String subtypeId: subtypes) {
+ builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
+ }
+ }
+
+ public InputMethodSettings(
+ Resources res, ContentResolver resolver,
+ HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList) {
+ mRes = res;
+ mResolver = resolver;
+ mMethodMap = methodMap;
+ mMethodList = methodList;
+ }
+
+ public List<InputMethodInfo> getEnabledInputMethodListLocked() {
+ return createEnabledInputMethodListLocked(
+ getEnabledInputMethodsAndSubtypeListLocked());
+ }
+
+ public List<Pair<InputMethodInfo, ArrayList<String>>>
+ getEnabledInputMethodAndSubtypeHashCodeListLocked() {
+ return createEnabledInputMethodAndSubtypeHashCodeListLocked(
+ getEnabledInputMethodsAndSubtypeListLocked());
+ }
+
+ public List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(
+ InputMethodInfo imi) {
+ List<Pair<String, ArrayList<String>>> imsList =
+ getEnabledInputMethodsAndSubtypeListLocked();
+ ArrayList<InputMethodSubtype> enabledSubtypes =
+ new ArrayList<InputMethodSubtype>();
+ if (imi != null) {
+ for (Pair<String, ArrayList<String>> imsPair : imsList) {
+ InputMethodInfo info = mMethodMap.get(imsPair.first);
+ if (info != null && info.getId().equals(imi.getId())) {
+ final int subtypeCount = info.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ InputMethodSubtype ims = info.getSubtypeAt(i);
+ for (String s: imsPair.second) {
+ if (String.valueOf(ims.hashCode()).equals(s)) {
+ enabledSubtypes.add(ims);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ return enabledSubtypes;
+ }
+
+ // At the initial boot, the settings for input methods are not set,
+ // so we need to enable IME in that case.
+ public void enableAllIMEsIfThereIsNoEnabledIME() {
+ if (TextUtils.isEmpty(getEnabledInputMethodsStr())) {
+ StringBuilder sb = new StringBuilder();
+ final int N = mMethodList.size();
+ for (int i = 0; i < N; i++) {
+ InputMethodInfo imi = mMethodList.get(i);
+ Slog.i(TAG, "Adding: " + imi.getId());
+ if (i > 0) sb.append(':');
+ sb.append(imi.getId());
+ }
+ putEnabledInputMethodsStr(sb.toString());
+ }
+ }
+
+ private List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
+ ArrayList<Pair<String, ArrayList<String>>> imsList
+ = new ArrayList<Pair<String, ArrayList<String>>>();
+ final String enabledInputMethodsStr = getEnabledInputMethodsStr();
+ if (TextUtils.isEmpty(enabledInputMethodsStr)) {
+ return imsList;
+ }
+ mInputMethodSplitter.setString(enabledInputMethodsStr);
+ while (mInputMethodSplitter.hasNext()) {
+ String nextImsStr = mInputMethodSplitter.next();
+ mSubtypeSplitter.setString(nextImsStr);
+ if (mSubtypeSplitter.hasNext()) {
+ ArrayList<String> subtypeHashes = new ArrayList<String>();
+ // The first element is ime id.
+ String imeId = mSubtypeSplitter.next();
+ while (mSubtypeSplitter.hasNext()) {
+ subtypeHashes.add(mSubtypeSplitter.next());
}
+ imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
+ }
+ }
+ return imsList;
+ }
+
+ public void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) {
+ if (reloadInputMethodStr) {
+ getEnabledInputMethodsStr();
+ }
+ if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) {
+ // Add in the newly enabled input method.
+ putEnabledInputMethodsStr(id);
+ } else {
+ putEnabledInputMethodsStr(
+ mEnabledInputMethodsStrCache + INPUT_METHOD_SEPARATER + id);
+ }
+ }
+
+ /**
+ * Build and put a string of EnabledInputMethods with removing specified Id.
+ * @return the specified id was removed or not.
+ */
+ public boolean buildAndPutEnabledInputMethodsStrRemovingIdLocked(
+ StringBuilder builder, List<Pair<String, ArrayList<String>>> imsList, String id) {
+ boolean isRemoved = false;
+ boolean needsAppendSeparator = false;
+ for (Pair<String, ArrayList<String>> ims: imsList) {
+ String curId = ims.first;
+ if (curId.equals(id)) {
// We are disabling this input method, and it is
// currently enabled. Skip it to remove from the
// new list.
- removed = true;
- } else if (!enabled) {
- // We are building a new list of input methods that
- // doesn't contain the given one.
- if (firstId == null) firstId = curId;
- if (builder.length() > 0) builder.append(':');
- builder.append(curId);
+ isRemoved = true;
+ } else {
+ if (needsAppendSeparator) {
+ builder.append(INPUT_METHOD_SEPARATER);
+ } else {
+ needsAppendSeparator = true;
+ }
+ buildEnabledInputMethodsSettingString(builder, ims);
}
}
+ if (isRemoved) {
+ // Update the setting with the new list of input methods.
+ putEnabledInputMethodsStr(builder.toString());
+ }
+ return isRemoved;
}
- if (!enabled) {
- if (!removed) {
- // We are disabling the input method but it is already
- // disabled. Nothing to do. The previous state was
- // disabled.
- return false;
+ private List<InputMethodInfo> createEnabledInputMethodListLocked(
+ List<Pair<String, ArrayList<String>>> imsList) {
+ final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
+ for (Pair<String, ArrayList<String>> ims: imsList) {
+ InputMethodInfo info = mMethodMap.get(ims.first);
+ if (info != null) {
+ res.add(info);
+ }
}
- // Update the setting with the new list of input methods.
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
- // We the disabled input method is currently selected, switch
- // to another one.
- String selId = Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD);
- if (id.equals(selId)) {
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD,
- firstId != null ? firstId : "");
+ return res;
+ }
+
+ private List<Pair<InputMethodInfo, ArrayList<String>>>
+ createEnabledInputMethodAndSubtypeHashCodeListLocked(
+ List<Pair<String, ArrayList<String>>> imsList) {
+ final ArrayList<Pair<InputMethodInfo, ArrayList<String>>> res
+ = new ArrayList<Pair<InputMethodInfo, ArrayList<String>>>();
+ for (Pair<String, ArrayList<String>> ims : imsList) {
+ InputMethodInfo info = mMethodMap.get(ims.first);
+ if (info != null) {
+ res.add(new Pair<InputMethodInfo, ArrayList<String>>(info, ims.second));
+ }
}
- // Previous state was enabled.
- return true;
+ return res;
}
- // Add in the newly enabled input method.
- if (enabledStr == null || enabledStr.length() == 0) {
- enabledStr = id;
- } else {
- enabledStr = enabledStr + ':' + id;
+ private void putEnabledInputMethodsStr(String str) {
+ Settings.Secure.putString(mResolver, Settings.Secure.ENABLED_INPUT_METHODS, str);
+ mEnabledInputMethodsStrCache = str;
}
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
+ private String getEnabledInputMethodsStr() {
+ mEnabledInputMethodsStrCache = Settings.Secure.getString(
+ mResolver, Settings.Secure.ENABLED_INPUT_METHODS);
+ if (DEBUG) {
+ Slog.d(TAG, "getEnabledInputMethodsStr: " + mEnabledInputMethodsStrCache);
+ }
+ return mEnabledInputMethodsStrCache;
+ }
- // Previous state was disabled.
- return false;
+ private void saveSubtypeHistory(
+ List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
+ StringBuilder builder = new StringBuilder();
+ boolean isImeAdded = false;
+ if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
+ builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
+ newSubtypeId);
+ isImeAdded = true;
+ }
+ for (Pair<String, String> ime: savedImes) {
+ String imeId = ime.first;
+ String subtypeId = ime.second;
+ if (TextUtils.isEmpty(subtypeId)) {
+ subtypeId = NOT_A_SUBTYPE_ID_STR;
+ }
+ if (isImeAdded) {
+ builder.append(INPUT_METHOD_SEPARATER);
+ } else {
+ isImeAdded = true;
+ }
+ builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATER).append(
+ subtypeId);
+ }
+ // Remove the last INPUT_METHOD_SEPARATER
+ putSubtypeHistoryStr(builder.toString());
+ }
+
+ public void addSubtypeToHistory(String imeId, String subtypeId) {
+ List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+ for (Pair<String, String> ime: subtypeHistory) {
+ if (ime.first.equals(imeId)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Subtype found in the history: " + imeId + ", "
+ + ime.second);
+ }
+ // We should break here
+ subtypeHistory.remove(ime);
+ break;
+ }
+ }
+ if (DEBUG) {
+ Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId);
+ }
+ saveSubtypeHistory(subtypeHistory, imeId, subtypeId);
+ }
+
+ private void putSubtypeHistoryStr(String str) {
+ if (DEBUG) {
+ Slog.d(TAG, "putSubtypeHistoryStr: " + str);
+ }
+ Settings.Secure.putString(
+ mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY, str);
+ }
+
+ public Pair<String, String> getLastInputMethodAndSubtypeLocked() {
+ // Gets the first one from the history
+ return getLastSubtypeForInputMethodLockedInternal(null);
+ }
+
+ public String getLastSubtypeForInputMethodLocked(String imeId) {
+ Pair<String, String> ime = getLastSubtypeForInputMethodLockedInternal(imeId);
+ if (ime != null) {
+ return ime.second;
+ } else {
+ return null;
+ }
+ }
+
+ private Pair<String, String> getLastSubtypeForInputMethodLockedInternal(String imeId) {
+ List<Pair<String, ArrayList<String>>> enabledImes =
+ getEnabledInputMethodsAndSubtypeListLocked();
+ List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+ for (Pair<String, String> imeAndSubtype: subtypeHistory) {
+ final String imeInTheHistory = imeAndSubtype.first;
+ // If imeId is empty, returns the first IME and subtype in the history
+ if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) {
+ final String subtypeInTheHistory = imeAndSubtype.second;
+ final String subtypeHashCode =
+ getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(
+ enabledImes, imeInTheHistory, subtypeInTheHistory);
+ if (!TextUtils.isEmpty(subtypeHashCode)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Enabled subtype found in the history: " + subtypeHashCode);
+ }
+ return new Pair<String, String>(imeInTheHistory, subtypeHashCode);
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "No enabled IME found in the history");
+ }
+ return null;
+ }
+
+ private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String,
+ ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) {
+ for (Pair<String, ArrayList<String>> enabledIme: enabledImes) {
+ if (enabledIme.first.equals(imeId)) {
+ final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second;
+ if (explicitlyEnabledSubtypes.size() == 0) {
+ // If there are no explicitly enabled subtypes, applicable subtypes are
+ // enabled implicitly.
+ InputMethodInfo ime = mMethodMap.get(imeId);
+ // If IME is enabled and no subtypes are enabled, applicable subtypes
+ // are enabled implicitly, so needs to treat them to be enabled.
+ if (ime != null && ime.getSubtypeCount() > 0) {
+ List<InputMethodSubtype> implicitlySelectedSubtypes =
+ getApplicableSubtypesLocked(mRes, getSubtypes(ime));
+ if (implicitlySelectedSubtypes != null) {
+ final int N = implicitlySelectedSubtypes.size();
+ for (int i = 0; i < N; ++i) {
+ final InputMethodSubtype st = implicitlySelectedSubtypes.get(i);
+ if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) {
+ return subtypeHashCode;
+ }
+ }
+ }
+ }
+ } else {
+ for (String s: explicitlyEnabledSubtypes) {
+ if (s.equals(subtypeHashCode)) {
+ // If both imeId and subtypeId are enabled, return subtypeId.
+ return s;
+ }
+ }
+ }
+ // If imeId was enabled but subtypeId was disabled.
+ return NOT_A_SUBTYPE_ID_STR;
+ }
+ }
+ // If both imeId and subtypeId are disabled, return null
+ return null;
+ }
+
+ private List<Pair<String, String>> loadInputMethodAndSubtypeHistoryLocked() {
+ ArrayList<Pair<String, String>> imsList = new ArrayList<Pair<String, String>>();
+ final String subtypeHistoryStr = getSubtypeHistoryStr();
+ if (TextUtils.isEmpty(subtypeHistoryStr)) {
+ return imsList;
+ }
+ mInputMethodSplitter.setString(subtypeHistoryStr);
+ while (mInputMethodSplitter.hasNext()) {
+ String nextImsStr = mInputMethodSplitter.next();
+ mSubtypeSplitter.setString(nextImsStr);
+ if (mSubtypeSplitter.hasNext()) {
+ String subtypeId = NOT_A_SUBTYPE_ID_STR;
+ // The first element is ime id.
+ String imeId = mSubtypeSplitter.next();
+ while (mSubtypeSplitter.hasNext()) {
+ subtypeId = mSubtypeSplitter.next();
+ break;
+ }
+ imsList.add(new Pair<String, String>(imeId, subtypeId));
+ }
+ }
+ return imsList;
+ }
+
+ private String getSubtypeHistoryStr() {
+ if (DEBUG) {
+ Slog.d(TAG, "getSubtypeHistoryStr: " + Settings.Secure.getString(
+ mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY));
+ }
+ return Settings.Secure.getString(
+ mResolver, Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY);
+ }
+
+ public void putSelectedInputMethod(String imeId) {
+ Settings.Secure.putString(mResolver, Settings.Secure.DEFAULT_INPUT_METHOD, imeId);
+ }
+
+ public void putSelectedSubtype(int subtypeId) {
+ Settings.Secure.putInt(
+ mResolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, subtypeId);
+ }
}
// ----------------------------------------------------------------------
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
index befc770..2c2cdfe 100644
--- a/services/java/com/android/server/InputWindow.java
+++ b/services/java/com/android/server/InputWindow.java
@@ -16,68 +16,63 @@
package com.android.server;
+import android.graphics.Region;
import android.view.InputChannel;
/**
* Describes input-related window properties for use by the input dispatcher.
- *
* @hide
*/
public final class InputWindow {
+ // The window handle.
+ public InputWindowHandle inputWindowHandle;
+
// The input channel associated with the window.
public InputChannel inputChannel;
-
+
// The window name.
public String name;
-
+
// Window layout params attributes. (WindowManager.LayoutParams)
public int layoutParamsFlags;
public int layoutParamsType;
-
+
// Dispatching timeout.
public long dispatchingTimeoutNanos;
-
- // Window frame area.
+
+ // Window frame.
public int frameLeft;
public int frameTop;
public int frameRight;
public int frameBottom;
-
- // Window visible frame area.
- public int visibleFrameLeft;
- public int visibleFrameTop;
- public int visibleFrameRight;
- public int visibleFrameBottom;
-
- // Window touchable area.
- public int touchableAreaLeft;
- public int touchableAreaTop;
- public int touchableAreaRight;
- public int touchableAreaBottom;
-
+
+ // Window touchable region.
+ public final Region touchableRegion = new Region();
+
// Window is visible.
public boolean visible;
-
+
// Window can receive keys.
public boolean canReceiveKeys;
-
+
// Window has focus.
public boolean hasFocus;
-
+
// Window has wallpaper. (window is the current wallpaper target)
public boolean hasWallpaper;
-
+
// Input event dispatching is paused.
public boolean paused;
-
+
// Window layer.
public int layer;
-
+
// Id of process and user that owns the window.
public int ownerPid;
public int ownerUid;
-
+
public void recycle() {
+ inputWindowHandle = null;
inputChannel = null;
}
}
diff --git a/services/java/com/android/server/InputWindowHandle.java b/services/java/com/android/server/InputWindowHandle.java
new file mode 100644
index 0000000..4b92939
--- /dev/null
+++ b/services/java/com/android/server/InputWindowHandle.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.view.WindowManagerPolicy;
+
+/**
+ * Functions as a handle for a window that can receive input.
+ * Enables the native input dispatcher to refer indirectly to the window manager's window state.
+ * @hide
+ */
+public final class InputWindowHandle {
+ // Pointer to the native input window handle.
+ // This field is lazily initialized via JNI.
+ @SuppressWarnings("unused")
+ private int ptr;
+
+ // The input application handle.
+ public final InputApplicationHandle inputApplicationHandle;
+
+ // The window manager's window state.
+ public final WindowManagerPolicy.WindowState windowState;
+
+ private native void nativeDispose();
+
+ public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
+ WindowManagerPolicy.WindowState windowState) {
+ this.inputApplicationHandle = inputApplicationHandle;
+ this.windowState = windowState;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ nativeDispose();
+ super.finalize();
+ }
+}
diff --git a/services/java/com/android/server/LightsService.java b/services/java/com/android/server/LightsService.java
index 9b57735..21f2bcf 100644
--- a/services/java/com/android/server/LightsService.java
+++ b/services/java/com/android/server/LightsService.java
@@ -24,12 +24,12 @@ import android.os.ServiceManager;
import android.os.Message;
import android.util.Slog;
-import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class LightsService {
private static final String TAG = "LightsService";
+ private static final boolean DEBUG = false;
static final int LIGHT_ID_BACKLIGHT = 0;
static final int LIGHT_ID_KEYBOARD = 1;
@@ -115,6 +115,8 @@ public class LightsService {
private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {
+ if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
+ + Integer.toHexString(color));
mColor = color;
mMode = mode;
mOnMS = onMS;
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 10107c6..656ec4d 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -479,14 +479,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mEnabledProviders.add(passiveProvider.getName());
// initialize external network location and geocoder services
- if (mNetworkLocationProviderPackageName != null) {
+ PackageManager pm = mContext.getPackageManager();
+ if (mNetworkLocationProviderPackageName != null &&
+ pm.resolveService(new Intent(mNetworkLocationProviderPackageName), 0) != null) {
mNetworkLocationProvider =
new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
mNetworkLocationProviderPackageName, mLocationHandler);
addProvider(mNetworkLocationProvider);
}
- if (mGeocodeProviderPackageName != null) {
+ if (mGeocodeProviderPackageName != null &&
+ pm.resolveService(new Intent(mGeocodeProviderPackageName), 0) != null) {
mGeocodeProvider = new GeocoderProxy(mContext, mGeocodeProviderPackageName);
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index a191549..3101222 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -19,6 +19,7 @@ package com.android.server;
import com.android.internal.app.IMediaContainerService;
import com.android.server.am.ActivityManagerService;
+import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -73,8 +74,8 @@ import javax.crypto.spec.PBEKeySpec;
* @hide - Applications should use android.os.storage.StorageManager
* to access the MountService.
*/
-class MountService extends IMountService.Stub
- implements INativeDaemonConnectorCallbacks {
+class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks {
+
private static final boolean LOCAL_LOGD = false;
private static final boolean DEBUG_UNMOUNT = false;
private static final boolean DEBUG_EVENTS = false;
@@ -151,6 +152,8 @@ class MountService extends IMountService.Stub
private boolean mBooted = false;
private boolean mReady = false;
private boolean mSendUmsConnectedOnBoot = false;
+ // true if we should fake MEDIA_MOUNTED state for external storage
+ private boolean mEmulateExternalStorage = false;
/**
* Private hash of currently mounted secure containers.
@@ -332,6 +335,7 @@ class MountService extends IMountService.Stub
super(l);
}
+ @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case H_UNMOUNT_PM_UPDATE: {
@@ -425,6 +429,7 @@ class MountService extends IMountService.Stub
}
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -440,12 +445,15 @@ class MountService extends IMountService.Stub
return;
}
new Thread() {
+ @Override
public void run() {
try {
String path = Environment.getExternalStorageDirectory().getPath();
String state = getVolumeState(path);
- if (state.equals(Environment.MEDIA_UNMOUNTED)) {
+ if (mEmulateExternalStorage) {
+ notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Mounted);
+ } else if (state.equals(Environment.MEDIA_UNMOUNTED)) {
int rc = doMountVolume(path);
if (rc != StorageResultCode.OperationSucceeded) {
Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc));
@@ -516,21 +524,21 @@ class MountService extends IMountService.Stub
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
return;
}
+ // Update state on PackageManager, but only of real events
+ if (!mEmulateExternalStorage) {
+ if (Environment.MEDIA_UNMOUNTED.equals(state)) {
+ mPms.updateExternalMediaStatus(false, false);
- if (Environment.MEDIA_UNMOUNTED.equals(state)) {
- // Tell the package manager the media is gone.
- mPms.updateExternalMediaStatus(false, false);
-
- /*
- * Some OBBs might have been unmounted when this volume was
- * unmounted, so send a message to the handler to let it know to
- * remove those from the list of mounted OBBS.
- */
- mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
- path));
- } else if (Environment.MEDIA_MOUNTED.equals(state)) {
- // Tell the package manager the media is available for use.
- mPms.updateExternalMediaStatus(true, false);
+ /*
+ * Some OBBs might have been unmounted when this volume was
+ * unmounted, so send a message to the handler to let it know to
+ * remove those from the list of mounted OBBS.
+ */
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
+ path));
+ } else if (Environment.MEDIA_MOUNTED.equals(state)) {
+ mPms.updateExternalMediaStatus(true, false);
+ }
}
String oldState = mLegacyState;
@@ -561,6 +569,7 @@ class MountService extends IMountService.Stub
* we need to do our work in a new thread.
*/
new Thread() {
+ @Override
public void run() {
/**
* Determine media state and UMS detection status
@@ -610,7 +619,7 @@ class MountService extends IMountService.Stub
Slog.w(TAG, "Failed to get share availability");
}
/*
- * Now that we've done our initialization, release
+ * Now that we've done our initialization, release
* the hounds!
*/
mReady = true;
@@ -674,6 +683,7 @@ class MountService extends IMountService.Stub
if (code == VoldResponseCode.VolumeDiskInserted) {
new Thread() {
+ @Override
public void run() {
try {
int rc;
@@ -1003,6 +1013,7 @@ class MountService extends IMountService.Stub
* USB mass storage disconnected while enabled
*/
new Thread() {
+ @Override
public void run() {
try {
int rc;
@@ -1040,6 +1051,13 @@ class MountService extends IMountService.Stub
public MountService(Context context) {
mContext = context;
+ mEmulateExternalStorage = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_emulateExternalStorage);
+ if (mEmulateExternalStorage) {
+ Slog.d(TAG, "using emulated external storage");
+ mLegacyState = Environment.MEDIA_MOUNTED;
+ }
+
// XXX: This will go away soon in favor of IMountServiceObserver
mPms = (PackageManagerService) ServiceManager.getService("package");
@@ -1144,6 +1162,17 @@ class MountService extends IMountService.Stub
// Post a unmount message.
ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
+ } else if (observer != null) {
+ /*
+ * Observer is waiting for onShutDownComplete when we are done.
+ * Since nothing will be done send notification directly so shutdown
+ * sequence can continue.
+ */
+ try {
+ observer.onShutDownComplete(StorageResultCode.OperationSucceeded);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException when shutting down");
+ }
}
}
@@ -1209,7 +1238,7 @@ class MountService extends IMountService.Stub
waitForReady();
return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
}
-
+
/**
* @return state of the volume at the specified mount point
*/
@@ -1226,6 +1255,10 @@ class MountService extends IMountService.Stub
return mLegacyState;
}
+ public boolean isExternalStorageEmulated() {
+ return mEmulateExternalStorage;
+ }
+
public int mountVolume(String path) {
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
@@ -1375,7 +1408,7 @@ class MountService extends IMountService.Stub
return rc;
}
-
+
public int mountSecureContainer(String id, String key, int ownerUid) {
validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
waitForReady();
@@ -1463,7 +1496,7 @@ class MountService extends IMountService.Stub
synchronized (mAsecMountSet) {
/*
- * Because a mounted container has active internal state which cannot be
+ * Because a mounted container has active internal state which cannot be
* changed while active, we must ensure both ids are not currently mounted.
*/
if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
@@ -1598,6 +1631,71 @@ class MountService extends IMountService.Stub
Slog.i(TAG, "Send to OBB handler: " + action.toString());
}
+ public int decryptStorage(String password) {
+ if (password == null) {
+ throw new IllegalArgumentException("password cannot be null");
+ }
+
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
+ "no permission to access the crypt keeper");
+
+ waitForReady();
+
+ if (DEBUG_EVENTS) {
+ Slog.i(TAG, "decrypting storage...");
+ }
+
+ try {
+ ArrayList<String> rsp = mConnector.doCommand("cryptfs checkpw " + password);
+ String []tok = rsp.get(0).split(" ");
+
+ if (tok == null || tok.length != 2) {
+ return -1;
+ }
+
+ int code = Integer.parseInt(tok[1]);
+
+ if (code == 0) {
+ // Decrypt was successful. Post a delayed message before restarting in order
+ // to let the UI to clear itself
+ mHandler.postDelayed(new Runnable() {
+ public void run() {
+ mConnector.doCommand(String.format("cryptfs restart"));
+ }
+ }, 2000); // 2 seconds
+ }
+
+ return code;
+ } catch (NativeDaemonConnectorException e) {
+ // Decryption failed
+ return e.getCode();
+ }
+ }
+
+ public int encryptStorage(String password) {
+ if (password == null) {
+ throw new IllegalArgumentException("password cannot be null");
+ }
+
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
+ "no permission to access the crypt keeper");
+
+ waitForReady();
+
+ if (DEBUG_EVENTS) {
+ Slog.i(TAG, "encrypting storage...");
+ }
+
+ try {
+ mConnector.doCommand(String.format("cryptfs enablecrypto inplace %s", password));
+ } catch (NativeDaemonConnectorException e) {
+ // Encryption failed
+ return e.getCode();
+ }
+
+ return 0;
+ }
+
private void addObbStateLocked(ObbState obbState) throws RemoteException {
final IBinder binder = obbState.getBinder();
List<ObbState> obbStates = mObbMounts.get(binder);
@@ -1885,6 +1983,7 @@ class MountService extends IMountService.Stub
mKey = key;
}
+ @Override
public void handleExecute() throws IOException, RemoteException {
waitForReady();
warnOnNotMounted();
@@ -1965,6 +2064,7 @@ class MountService extends IMountService.Stub
}
}
+ @Override
public void handleError() {
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
}
@@ -1994,6 +2094,7 @@ class MountService extends IMountService.Stub
mForceUnmount = force;
}
+ @Override
public void handleExecute() throws IOException {
waitForReady();
warnOnNotMounted();
@@ -2048,6 +2149,7 @@ class MountService extends IMountService.Stub
}
}
+ @Override
public void handleError() {
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index f0acdc0..152605f 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -219,28 +219,6 @@ class NetworkManagementService extends INetworkManagementService.Stub {
}
}
- private static int stringToIpAddr(String addrString) throws UnknownHostException {
- try {
- String[] parts = addrString.split("\\.");
- if (parts.length != 4) {
- throw new UnknownHostException(addrString);
- }
-
- int a = Integer.parseInt(parts[0]) ;
- int b = Integer.parseInt(parts[1]) << 8;
- int c = Integer.parseInt(parts[2]) << 16;
- int d = Integer.parseInt(parts[3]) << 24;
-
- return a | b | c | d;
- } catch (NumberFormatException ex) {
- throw new UnknownHostException(addrString);
- }
- }
-
- public static String intToIpString(int i) {
- return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." +
- (i & 0xFF);
- }
//
// INetworkManagementService members
@@ -288,18 +266,17 @@ class NetworkManagementService extends INetworkManagementService.Stub {
cfg = new InterfaceConfiguration();
cfg.hwAddr = st.nextToken(" ");
try {
- cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
+ cfg.addr = InetAddress.getByName(st.nextToken(" "));
} catch (UnknownHostException uhe) {
Slog.e(TAG, "Failed to parse ipaddr", uhe);
- cfg.ipAddr = 0;
}
try {
- cfg.netmask = stringToIpAddr(st.nextToken(" "));
+ cfg.mask = InetAddress.getByName(st.nextToken(" "));
} catch (UnknownHostException uhe) {
Slog.e(TAG, "Failed to parse netmask", uhe);
- cfg.netmask = 0;
}
+
cfg.interfaceFlags = st.nextToken("]").trim() +"]";
} catch (NoSuchElementException nsee) {
throw new IllegalStateException(
@@ -312,12 +289,13 @@ class NetworkManagementService extends INetworkManagementService.Stub {
public void setInterfaceConfig(
String iface, InterfaceConfiguration cfg) throws IllegalStateException {
String cmd = String.format("interface setcfg %s %s %s %s", iface,
- intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
+ cfg.addr.getHostAddress(), cfg.mask.getHostAddress(),
+ cfg.interfaceFlags);
try {
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) {
throw new IllegalStateException(
- "Unable to communicate with native daemon to interface setcfg");
+ "Unable to communicate with native daemon to interface setcfg - " + e);
}
}
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
new file mode 100644
index 0000000..52f84eb
--- /dev/null
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -0,0 +1,297 @@
+/*
+ * 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;
+
+import com.android.internal.telephony.TelephonyIntents;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.SntpClient;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Monitors the network time and updates the system time if it is out of sync
+ * and there hasn't been any NITZ update from the carrier recently.
+ * If looking up the network time fails for some reason, it tries a few times with a short
+ * interval and then resets to checking on longer intervals.
+ * <p>
+ * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
+ * available.
+ * </p>
+ */
+public class NetworkTimeUpdateService {
+
+ private static final String TAG = "NetworkTimeUpdateService";
+ private static final boolean DBG = false;
+
+ private static final int EVENT_AUTO_TIME_CHANGED = 1;
+ private static final int EVENT_POLL_NETWORK_TIME = 2;
+
+ /** Normal polling frequency */
+ private static final long POLLING_INTERVAL_MS = 24L * 60 * 60 * 1000; // 24 hrs
+ /** Try-again polling interval, in case the network request failed */
+ private static final long POLLING_INTERVAL_SHORTER_MS = 60 * 1000L; // 60 seconds
+ /** Number of times to try again */
+ private static final int TRY_AGAIN_TIMES_MAX = 3;
+ /** How long to wait for the NTP server to respond. */
+ private static final int MAX_NTP_FETCH_WAIT_MS = 20 * 1000;
+ /** If the time difference is greater than this threshold, then update the time. */
+ private static final int TIME_ERROR_THRESHOLD_MS = 5 * 1000;
+
+ private static final String ACTION_POLL =
+ "com.android.server.NetworkTimeUpdateService.action.POLL";
+ private static final String PROPERTIES_FILE = "/etc/gps.conf";
+ private static int POLL_REQUEST = 0;
+
+ private static final long NOT_SET = -1;
+ private long mNitzTimeSetTime = NOT_SET;
+ // TODO: Have a way to look up the timezone we are in
+ private long mNitzZoneSetTime = NOT_SET;
+
+ private Context mContext;
+ // NTP lookup is done on this thread and handler
+ private Handler mHandler;
+ private HandlerThread mThread;
+ private AlarmManager mAlarmManager;
+ private PendingIntent mPendingPollIntent;
+ private SettingsObserver mSettingsObserver;
+ // Address of the NTP server
+ private String mNtpServer;
+ // The last time that we successfully fetched the NTP time.
+ private long mLastNtpFetchTime = NOT_SET;
+ // Keeps track of how many quick attempts were made to fetch NTP time.
+ // During bootup, the network may not have been up yet, or it's taking time for the
+ // connection to happen.
+ private int mTryAgainCounter;
+
+ public NetworkTimeUpdateService(Context context) {
+ mContext = context;
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ Intent pollIntent = new Intent(ACTION_POLL, null);
+ mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+ }
+
+ /** Initialize the receivers and initiate the first NTP request */
+ public void systemReady() {
+ mNtpServer = getNtpServerAddress();
+ if (mNtpServer == null) {
+ Slog.e(TAG, "NTP server address not found, not syncing to NTP time");
+ return;
+ }
+
+ registerForTelephonyIntents();
+ registerForAlarms();
+
+ mThread = new HandlerThread(TAG);
+ mThread.start();
+ mHandler = new MyHandler(mThread.getLooper());
+ // Check the network time on the new thread
+ mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
+ mSettingsObserver.observe(mContext);
+ }
+
+ private String getNtpServerAddress() {
+ String serverAddress = null;
+ FileInputStream stream = null;
+ try {
+ Properties properties = new Properties();
+ File file = new File(PROPERTIES_FILE);
+ stream = new FileInputStream(file);
+ properties.load(stream);
+ serverAddress = properties.getProperty("NTP_SERVER", null);
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (Exception e) {}
+ }
+ }
+ return serverAddress;
+ }
+
+ private void registerForTelephonyIntents() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+ intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
+ mContext.registerReceiver(mNitzReceiver, intentFilter);
+ }
+
+ private void registerForAlarms() {
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+ }
+ }, new IntentFilter(ACTION_POLL));
+ }
+
+ private void onPollNetworkTime(int event) {
+ // If Automatic time is not set, don't bother.
+ if (!isAutomaticTimeRequested()) return;
+
+ final long refTime = SystemClock.elapsedRealtime();
+ // If NITZ time was received less than POLLING_INTERVAL_MS time ago,
+ // no need to sync to NTP.
+ if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < POLLING_INTERVAL_MS) {
+ resetAlarm(POLLING_INTERVAL_MS);
+ return;
+ }
+ final long currentTime = System.currentTimeMillis();
+ if (DBG) Log.d(TAG, "System time = " + currentTime);
+ // Get the NTP time
+ if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + POLLING_INTERVAL_MS
+ || event == EVENT_AUTO_TIME_CHANGED) {
+ if (DBG) Log.d(TAG, "Before Ntp fetch");
+ long ntp = getNtpTime();
+ if (DBG) Log.d(TAG, "Ntp = " + ntp);
+ if (ntp > 0) {
+ mTryAgainCounter = 0;
+ mLastNtpFetchTime = SystemClock.elapsedRealtime();
+ if (Math.abs(ntp - currentTime) > TIME_ERROR_THRESHOLD_MS) {
+ // Set the system time
+ if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
+ // Make sure we don't overflow, since it's going to be converted to an int
+ if (ntp / 1000 < Integer.MAX_VALUE) {
+ SystemClock.setCurrentTimeMillis(ntp);
+ }
+ } else {
+ if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
+ }
+ } else {
+ // Try again shortly
+ mTryAgainCounter++;
+ if (mTryAgainCounter <= TRY_AGAIN_TIMES_MAX) {
+ resetAlarm(POLLING_INTERVAL_SHORTER_MS);
+ } else {
+ // Try much later
+ mTryAgainCounter = 0;
+ resetAlarm(POLLING_INTERVAL_MS);
+ }
+ return;
+ }
+ }
+ resetAlarm(POLLING_INTERVAL_MS);
+ }
+
+ /**
+ * Cancel old alarm and starts a new one for the specified interval.
+ *
+ * @param interval when to trigger the alarm, starting from now.
+ */
+ private void resetAlarm(long interval) {
+ mAlarmManager.cancel(mPendingPollIntent);
+ long now = SystemClock.elapsedRealtime();
+ long next = now + interval;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+ }
+
+ private long getNtpTime() {
+ SntpClient client = new SntpClient();
+ if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT_MS)) {
+ return client.getNtpTime();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Checks if the user prefers to automatically set the time.
+ */
+ private boolean isAutomaticTimeRequested() {
+ return Settings.System.getInt(mContext.getContentResolver(), Settings.System.AUTO_TIME, 0)
+ != 0;
+ }
+
+ /** Receiver for Nitz time events */
+ private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
+ mNitzTimeSetTime = SystemClock.elapsedRealtime();
+ } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
+ mNitzZoneSetTime = SystemClock.elapsedRealtime();
+ }
+ }
+ };
+
+ /** Handler to do the network accesses on */
+ private class MyHandler extends Handler {
+
+ public MyHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_AUTO_TIME_CHANGED:
+ case EVENT_POLL_NETWORK_TIME:
+ onPollNetworkTime(msg.what);
+ break;
+ }
+ }
+ }
+
+ /** Observer to watch for changes to the AUTO_TIME setting */
+ private static class SettingsObserver extends ContentObserver {
+
+ private int mMsg;
+ private Handler mHandler;
+
+ SettingsObserver(Handler handler, int msg) {
+ super(handler);
+ mHandler = handler;
+ mMsg = msg;
+ }
+
+ void observe(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.registerContentObserver(Settings.System.getUriFor(Settings.System.AUTO_TIME),
+ false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.obtainMessage(mMsg).sendToTarget();
+ }
+ }
+}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index e8c1613..0490190 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -17,7 +17,6 @@
package com.android.server;
import com.android.internal.statusbar.StatusBarNotification;
-import com.android.server.StatusBarManagerService;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
@@ -38,16 +37,14 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.hardware.Usb;
+import android.hardware.UsbManager;
import android.media.AudioManager;
import android.net.Uri;
-import android.os.BatteryManager;
-import android.os.Bundle;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
-import android.os.Power;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -56,8 +53,8 @@ import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.EventLog;
-import android.util.Slog;
import android.util.Log;
+import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
@@ -91,8 +88,6 @@ public class NotificationManagerService extends INotificationManager.Stub
private WorkerHandler mHandler;
private StatusBarManagerService mStatusBar;
- private LightsService mLightsService;
- private LightsService.Light mBatteryLight;
private LightsService.Light mNotificationLight;
private LightsService.Light mAttentionLight;
@@ -127,18 +122,8 @@ public class NotificationManagerService extends INotificationManager.Stub
private ArrayList<ToastRecord> mToastQueue;
private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
-
- private boolean mBatteryCharging;
- private boolean mBatteryLow;
- private boolean mBatteryFull;
private NotificationRecord mLedNotification;
- private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
- private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on
- private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
- private static final int BATTERY_BLINK_ON = 125;
- private static final int BATTERY_BLINK_OFF = 2875;
-
private static String idDebugString(Context baseContext, String packageName, int id) {
Context c = null;
@@ -171,8 +156,6 @@ public class NotificationManagerService extends INotificationManager.Stub
final int id;
final int uid;
final int initialPid;
- ITransientNotification callback;
- int duration;
final Notification notification;
IBinder statusBarKey;
@@ -282,7 +265,13 @@ public class NotificationManagerService extends INotificationManager.Stub
public void onNotificationClick(String pkg, String tag, int id) {
cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
- Notification.FLAG_FOREGROUND_SERVICE);
+ Notification.FLAG_FOREGROUND_SERVICE, true);
+ }
+
+ public void onNotificationClear(String pkg, String tag, int id) {
+ cancelNotification(pkg, tag, id, 0,
+ Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+ true);
}
public void onPanelRevealed() {
@@ -318,7 +307,7 @@ public class NotificationManagerService extends INotificationManager.Stub
int uid, int initialPid, String message) {
Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
+ "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
- cancelNotification(pkg, tag, id, 0, 0);
+ cancelNotification(pkg, tag, id, 0, 0, false);
long ident = Binder.clearCallingIdentity();
try {
ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
@@ -337,29 +326,12 @@ public class NotificationManagerService extends INotificationManager.Stub
boolean queryRestart = false;
- if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0);
- int level = intent.getIntExtra("level", -1);
- boolean batteryLow = (level >= 0 && level <= Power.LOW_BATTERY_THRESHOLD);
- int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
- boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90);
-
- if (batteryCharging != mBatteryCharging ||
- batteryLow != mBatteryLow ||
- batteryFull != mBatteryFull) {
- mBatteryCharging = batteryCharging;
- mBatteryLow = batteryLow;
- mBatteryFull = batteryFull;
- updateLights();
- }
- } else if (action.equals(Usb.ACTION_USB_STATE)) {
+ if (action.equals(UsbManager.ACTION_USB_STATE)) {
Bundle extras = intent.getExtras();
- boolean usbConnected = extras.getBoolean(Usb.USB_CONNECTED);
- boolean adbEnabled = (Usb.USB_FUNCTION_ENABLED.equals(
- extras.getString(Usb.USB_FUNCTION_ADB)));
+ boolean usbConnected = extras.getBoolean(UsbManager.USB_CONNECTED);
+ boolean adbEnabled = (UsbManager.USB_FUNCTION_ENABLED.equals(
+ extras.getString(UsbManager.USB_FUNCTION_ADB)));
updateAdbNotification(usbConnected && adbEnabled);
- } else if (action.equals(Usb.ACTION_USB_DISCONNECTED)) {
- updateAdbNotification(false);
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)
|| (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
@@ -430,7 +402,6 @@ public class NotificationManagerService extends INotificationManager.Stub
{
super();
mContext = context;
- mLightsService = lights;
mAm = ActivityManagerNative.getDefault();
mSound = new NotificationPlayer(TAG);
mSound.setUsesWakeLock(context);
@@ -440,7 +411,6 @@ public class NotificationManagerService extends INotificationManager.Stub
mStatusBar = statusBar;
statusBar.setNotificationCallbacks(mNotificationCallbacks);
- mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
@@ -461,10 +431,9 @@ public class NotificationManagerService extends INotificationManager.Stub
mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
}
- // register for battery changed notifications
+ // register for various Intents
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Usb.ACTION_USB_STATE);
+ filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
@@ -667,6 +636,7 @@ public class NotificationManagerService extends INotificationManager.Stub
// Notifications
// ============================================================================
+ @Deprecated
public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
{
enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
@@ -722,10 +692,6 @@ public class NotificationManagerService extends INotificationManager.Stub
throw new IllegalArgumentException("contentView required: pkg=" + pkg
+ " id=" + id + " notification=" + notification);
}
- if (notification.contentIntent == null) {
- throw new IllegalArgumentException("contentIntent required: pkg=" + pkg
- + " id=" + id + " notification=" + notification);
- }
}
synchronized (mNotificationList) {
@@ -883,7 +849,20 @@ public class NotificationManagerService extends INotificationManager.Stub
manager.sendAccessibilityEvent(event);
}
- private void cancelNotificationLocked(NotificationRecord r) {
+ private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
+ // tell the app
+ if (sendDelete) {
+ if (r.notification.deleteIntent != null) {
+ try {
+ r.notification.deleteIntent.send();
+ } catch (PendingIntent.CanceledException ex) {
+ // do nothing - there's no relevant way to recover, and
+ // no reason to let this propagate
+ Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
+ }
+ }
+ }
+
// status bar
if (r.notification.icon != 0) {
long identity = Binder.clearCallingIdentity();
@@ -932,7 +911,7 @@ public class NotificationManagerService extends INotificationManager.Stub
* and none of the {@code mustNotHaveFlags}.
*/
private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
- int mustNotHaveFlags) {
+ int mustNotHaveFlags, boolean sendDelete) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);
synchronized (mNotificationList) {
@@ -949,7 +928,7 @@ public class NotificationManagerService extends INotificationManager.Stub
mNotificationList.remove(index);
- cancelNotificationLocked(r);
+ cancelNotificationLocked(r, sendDelete);
updateLightsLocked();
}
}
@@ -982,7 +961,7 @@ public class NotificationManagerService extends INotificationManager.Stub
return true;
}
mNotificationList.remove(i);
- cancelNotificationLocked(r);
+ cancelNotificationLocked(r, false);
}
if (canceledSomething) {
updateLightsLocked();
@@ -991,7 +970,7 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
-
+ @Deprecated
public void cancelNotification(String pkg, int id) {
cancelNotificationWithTag(pkg, null /* tag */, id);
}
@@ -1001,7 +980,7 @@ public class NotificationManagerService extends INotificationManager.Stub
// Don't allow client applications to cancel foreground service notis.
cancelNotification(pkg, tag, id, 0,
Binder.getCallingUid() == Process.SYSTEM_UID
- ? 0 : Notification.FLAG_FOREGROUND_SERVICE);
+ ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false);
}
public void cancelAllNotifications(String pkg) {
@@ -1037,17 +1016,8 @@ public class NotificationManagerService extends INotificationManager.Stub
if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR)) == 0) {
- if (r.notification.deleteIntent != null) {
- try {
- r.notification.deleteIntent.send();
- } catch (PendingIntent.CanceledException ex) {
- // do nothing - there's no relevant way to recover, and
- // no reason to let this propagate
- Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
- }
- }
mNotificationList.remove(i);
- cancelNotificationLocked(r);
+ cancelNotificationLocked(r, true);
}
}
@@ -1055,34 +1025,9 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
- private void updateLights() {
- synchronized (mNotificationList) {
- updateLightsLocked();
- }
- }
-
// lock on mNotificationList
private void updateLightsLocked()
{
- // Battery low always shows, other states only show if charging.
- if (mBatteryLow) {
- if (mBatteryCharging) {
- mBatteryLight.setColor(BATTERY_LOW_ARGB);
- } else {
- // Flash when battery is low and not charging
- mBatteryLight.setFlashing(BATTERY_LOW_ARGB, LightsService.LIGHT_FLASH_TIMED,
- BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
- }
- } else if (mBatteryCharging) {
- if (mBatteryFull) {
- mBatteryLight.setColor(BATTERY_FULL_ARGB);
- } else {
- mBatteryLight.setColor(BATTERY_MEDIUM_ARGB);
- }
- } else {
- mBatteryLight.turnOff();
- }
-
// clear pending pulse notification if screen is on
if (mScreenOn || mLedNotification == null) {
mPendingPulseNotification = false;
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 9b8a605..94312fc 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -1050,7 +1050,7 @@ class PackageManagerService extends IPackageManager.Stub {
void cleanupInstallFailedPackage(PackageSetting ps) {
Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
if (mInstaller != null) {
- boolean useSecureFS = useEncryptedFilesystemForPackage(ps.pkg);
+ boolean useSecureFS = false;
int retCode = mInstaller.remove(ps.name, useSecureFS);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data directory for package: "
@@ -1225,6 +1225,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ permReader.close();
} catch (XmlPullParserException e) {
Slog.w(TAG, "Got execption parsing permissions.", e);
} catch (IOException e) {
@@ -2779,11 +2780,6 @@ 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) {
Slog.w(TAG, "Unable to update from " + oldPkg.name
@@ -2800,7 +2796,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
private File getDataPathForPackage(PackageParser.Package pkg) {
- boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
+ boolean useEncryptedFSDir = false;
File dataPath;
if (useEncryptedFSDir) {
dataPath = new File(mSecureAppDataDir, pkg.packageName);
@@ -2852,7 +2848,7 @@ class PackageManagerService extends IPackageManager.Stub {
mResolveActivity.processName = mAndroidApplication.processName;
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
- mResolveActivity.theme = com.android.internal.R.style.Theme_Dialog_Alert;
+ mResolveActivity.theme = com.android.internal.R.style.Theme_Holo_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
mResolveInfo.activityInfo = mResolveActivity;
@@ -2926,24 +2922,23 @@ class PackageManagerService extends IPackageManager.Stub {
System.arraycopy(mTmpSharedLibraries, 0,
pkg.usesLibraryFiles, 0, num);
}
+ }
- if (pkg.reqFeatures != null) {
- N = pkg.reqFeatures.size();
- for (int i=0; i<N; i++) {
- FeatureInfo fi = pkg.reqFeatures.get(i);
- if ((fi.flags&FeatureInfo.FLAG_REQUIRED) == 0) {
- // Don't care.
- continue;
- }
+ if (pkg.reqFeatures != null) {
+ int N = pkg.reqFeatures.size();
+ for (int i = 0; i < N; i++) {
+ FeatureInfo fi = pkg.reqFeatures.get(i);
+ if ((fi.flags & FeatureInfo.FLAG_REQUIRED) == 0) {
+ // Don't care.
+ continue;
+ }
- if (fi.name != null) {
- if (mAvailableFeatures.get(fi.name) == null) {
- Slog.e(TAG, "Package " + pkg.packageName
- + " requires unavailable feature "
- + fi.name + "; failing!");
- mLastScanError = PackageManager.INSTALL_FAILED_MISSING_FEATURE;
- return null;
- }
+ if (fi.name != null) {
+ if (mAvailableFeatures.get(fi.name) == null) {
+ Slog.e(TAG, "Package " + pkg.packageName
+ + " requires unavailable feature " + fi.name + "; failing!");
+ mLastScanError = PackageManager.INSTALL_FAILED_MISSING_FEATURE;
+ return null;
}
}
}
@@ -3157,7 +3152,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);
+ boolean useEncryptedFSDir = false;
dataPath = getDataPathForPackage(pkg);
boolean uidError = false;
@@ -4412,10 +4407,19 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
}
-
+
+ /**
+ * Check if the external storage media is available. This is true if there
+ * is a mounted external storage medium or if the external storage is
+ * emulated.
+ */
+ private boolean isExternalMediaAvailable() {
+ return mMediaMounted || Environment.isExternalStorageEmulated();
+ }
+
public String nextPackageToClean(String lastPackage) {
synchronized (mPackages) {
- if (!mMediaMounted) {
+ if (!isExternalMediaAvailable()) {
// If the external storage is no longer mounted at this point,
// the caller may not have been able to delete all of this
// packages files and can not delete any more. Bail.
@@ -4435,7 +4439,7 @@ class PackageManagerService extends IPackageManager.Stub {
void startCleaningPackages() {
synchronized (mPackages) {
- if (!mMediaMounted) {
+ if (!isExternalMediaAvailable()) {
return;
}
if (mSettings.mPackagesToBeCleaned.size() <= 0) {
@@ -4570,6 +4574,80 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
+ public void setInstallerPackageName(String targetPackage,
+ String installerPackageName) {
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ final int permission = mContext.checkCallingPermission(
+ android.Manifest.permission.INSTALL_PACKAGES);
+ final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ synchronized (mPackages) {
+ PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage);
+ if (targetPackageSetting == null) {
+ throw new IllegalArgumentException("Unknown target package: " + targetPackage);
+ }
+
+ PackageSetting installerPackageSetting;
+ if (installerPackageName != null) {
+ installerPackageSetting = mSettings.mPackages.get(installerPackageName);
+ if (installerPackageSetting == null) {
+ throw new IllegalArgumentException("Unknown installer package: "
+ + installerPackageName);
+ }
+ } else {
+ installerPackageSetting = null;
+ }
+
+ Signature[] callerSignature;
+ Object obj = mSettings.getUserIdLP(uid);
+ if (obj != null) {
+ if (obj instanceof SharedUserSetting) {
+ callerSignature = ((SharedUserSetting)obj).signatures.mSignatures;
+ } else if (obj instanceof PackageSetting) {
+ callerSignature = ((PackageSetting)obj).signatures.mSignatures;
+ } else {
+ throw new SecurityException("Bad object " + obj + " for uid " + uid);
+ }
+ } else {
+ throw new SecurityException("Unknown calling uid " + uid);
+ }
+
+ // Verify: can't set installerPackageName to a package that is
+ // not signed with the same cert as the caller.
+ if (installerPackageSetting != null) {
+ if (checkSignaturesLP(callerSignature,
+ installerPackageSetting.signatures.mSignatures)
+ != PackageManager.SIGNATURE_MATCH) {
+ throw new SecurityException(
+ "Caller does not have same cert as new installer package "
+ + installerPackageName);
+ }
+ }
+
+ // Verify: if target already has an installer package, it must
+ // be signed with the same cert as the caller.
+ if (targetPackageSetting.installerPackageName != null) {
+ PackageSetting setting = mSettings.mPackages.get(
+ targetPackageSetting.installerPackageName);
+ // If the currently set package isn't valid, then it's always
+ // okay to change it.
+ if (setting != null) {
+ if (checkSignaturesLP(callerSignature,
+ setting.signatures.mSignatures)
+ != PackageManager.SIGNATURE_MATCH) {
+ throw new SecurityException(
+ "Caller does not have same cert as old installer package "
+ + targetPackageSetting.installerPackageName);
+ }
+ }
+ }
+
+ // Okay!
+ targetPackageSetting.installerPackageName = installerPackageName;
+ scheduleWriteSettingsLocked();
+ }
+ }
+
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
@@ -4677,6 +4755,79 @@ class PackageManagerService extends IPackageManager.Stub {
abstract void handleReturnCode();
}
+ class MeasureParams extends HandlerParams {
+ private final PackageStats mStats;
+ private boolean mSuccess;
+
+ private final IPackageStatsObserver mObserver;
+
+ public MeasureParams(PackageStats stats, boolean success,
+ IPackageStatsObserver observer) {
+ mObserver = observer;
+ mStats = stats;
+ mSuccess = success;
+ }
+
+ @Override
+ void handleStartCopy() throws RemoteException {
+ final boolean mounted;
+
+ if (Environment.isExternalStorageEmulated()) {
+ mounted = true;
+ } else {
+ final String status = Environment.getExternalStorageState();
+
+ mounted = status.equals(Environment.MEDIA_MOUNTED)
+ || status.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
+ }
+
+ if (mounted) {
+ final File externalCacheDir = Environment
+ .getExternalStorageAppCacheDirectory(mStats.packageName);
+ final long externalCacheSize = mContainerService
+ .calculateDirectorySize(externalCacheDir.getPath());
+ mStats.externalCacheSize = externalCacheSize;
+
+ final File externalDataDir = Environment
+ .getExternalStorageAppDataDirectory(mStats.packageName);
+ long externalDataSize = mContainerService.calculateDirectorySize(externalDataDir
+ .getPath());
+
+ if (externalCacheDir.getParentFile().equals(externalDataDir)) {
+ externalDataSize -= externalCacheSize;
+ }
+ mStats.externalDataSize = externalDataSize;
+
+ final File externalMediaDir = Environment
+ .getExternalStorageAppMediaDirectory(mStats.packageName);
+ mStats.externalMediaSize = mContainerService
+ .calculateDirectorySize(externalCacheDir.getPath());
+
+ final File externalObbDir = Environment
+ .getExternalStorageAppObbDirectory(mStats.packageName);
+ mStats.externalObbSize = mContainerService.calculateDirectorySize(externalObbDir
+ .getPath());
+ }
+ }
+
+ @Override
+ void handleReturnCode() {
+ if (mObserver != null) {
+ try {
+ mObserver.onGetStatsCompleted(mStats, mSuccess);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
+ }
+ }
+ }
+
+ @Override
+ void handleServiceError() {
+ Slog.e(TAG, "Could not measure application " + mStats.packageName
+ + " external storage");
+ }
+ }
+
class InstallParams extends HandlerParams {
final IPackageInstallObserver observer;
int flags;
@@ -6162,7 +6313,7 @@ class PackageManagerService extends IPackageManager.Stub {
deletedPs = mSettings.mPackages.get(packageName);
}
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
- boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
+ boolean useEncryptedFSDir = false;
if (mInstaller != null) {
int retCode = mInstaller.remove(packageName, useEncryptedFSDir);
if (retCode < 0) {
@@ -6406,7 +6557,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
boolean useEncryptedFSDir = false;
- if(!dataOnly) {
+ if (!dataOnly) {
//need to check this only for fully installed applications
if (p == null) {
Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
@@ -6417,7 +6568,6 @@ 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, useEncryptedFSDir);
@@ -6471,7 +6621,7 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
- boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
+ boolean useEncryptedFSDir = false;
if (mInstaller != null) {
int retCode = mInstaller.deleteCacheFiles(packageName, useEncryptedFSDir);
if (retCode < 0) {
@@ -6491,18 +6641,16 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
- PackageStats lStats = new PackageStats(packageName);
- final boolean succeded;
+ PackageStats stats = new PackageStats(packageName);
+
+ final boolean success;
synchronized (mInstallLock) {
- succeded = getPackageSizeInfoLI(packageName, lStats);
+ success = getPackageSizeInfoLI(packageName, stats);
}
- if(observer != null) {
- try {
- observer.onGetStatsCompleted(lStats, succeded);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
- } //end if observer
+
+ Message msg = mHandler.obtainMessage(INIT_COPY);
+ msg.obj = new MeasureParams(stats, success, observer);
+ mHandler.sendMessage(msg);
} //end run
});
}
@@ -6535,7 +6683,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null;
}
- boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
+ boolean useEncryptedFSDir = false;
if (mInstaller != null) {
int res = mInstaller.getSizeInfo(packageName, p.mPath,
publicSrcDir, pStats, useEncryptedFSDir);
@@ -7299,7 +7447,7 @@ class PackageManagerService extends IPackageManager.Stub {
pw.println(" ");
pw.println("Package warning messages:");
File fname = getSettingsProblemFile();
- FileInputStream in;
+ FileInputStream in = null;
try {
in = new FileInputStream(fname);
int avail = in.available();
@@ -7308,6 +7456,13 @@ class PackageManagerService extends IPackageManager.Stub {
pw.print(new String(data));
} catch (FileNotFoundException e) {
} catch (IOException e) {
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
}
}
}
@@ -7700,8 +7855,7 @@ class PackageManagerService extends IPackageManager.Stub {
this.pkgFlags = pkgFlags & (
ApplicationInfo.FLAG_SYSTEM |
ApplicationInfo.FLAG_FORWARD_LOCK |
- ApplicationInfo.FLAG_EXTERNAL_STORAGE |
- ApplicationInfo.FLAG_NEVER_ENCRYPT);
+ ApplicationInfo.FLAG_EXTERNAL_STORAGE);
}
}
@@ -7889,6 +8043,14 @@ class PackageManagerService extends IPackageManager.Stub {
sharedUser = orig.sharedUser;
}
+ public void copyFrom(PackageSetting base) {
+ super.copyFrom((PackageSettingBase) base);
+
+ userId = base.userId;
+ sharedUser = base.sharedUser;
+ pkg = base.pkg;
+ }
+
@Override
public String toString() {
return "PackageSetting{"
@@ -9736,7 +9898,8 @@ class PackageManagerService extends IPackageManager.Stub {
* Update media status on PackageManager.
*/
public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
throw new SecurityException("Media status can only be updated by the system");
}
synchronized (mPackages) {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index a6daaef..caf6376 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -219,6 +219,8 @@ class PowerManagerService extends IPowerManager.Stub
private float mLightSensorValue = -1;
private boolean mProxIgnoredBecauseScreenTurnedOff = false;
private int mHighestLightSensorValue = -1;
+ private boolean mLightSensorPendingDecrease = false;
+ private boolean mLightSensorPendingIncrease = false;
private float mLightSensorPendingValue = -1;
private int mLightSensorScreenBrightness = -1;
private int mLightSensorButtonBrightness = -1;
@@ -1141,6 +1143,8 @@ class PowerManagerService extends IPowerManager.Stub
pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
pw.println(" mLightSensorValue=" + mLightSensorValue
+ " mLightSensorPendingValue=" + mLightSensorPendingValue);
+ pw.println(" mLightSensorPendingDecrease=" + mLightSensorPendingDecrease
+ + " mLightSensorPendingIncrease=" + mLightSensorPendingIncrease);
pw.println(" mLightSensorScreenBrightness=" + mLightSensorScreenBrightness
+ " mLightSensorButtonBrightness=" + mLightSensorButtonBrightness
+ " mLightSensorKeyboardBrightness=" + mLightSensorKeyboardBrightness);
@@ -1171,10 +1175,8 @@ class PowerManagerService extends IPowerManager.Stub
pw.println("mPokeLocks.size=" + mPokeLocks.size() + ":");
for (PokeLock p: mPokeLocks.values()) {
pw.println(" poke lock '" + p.tag + "':"
- + ((p.pokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0
- ? " POKE_LOCK_IGNORE_CHEEK_EVENTS" : "")
- + ((p.pokey & POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS) != 0
- ? " POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS" : "")
+ + ((p.pokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0
+ ? " POKE_LOCK_IGNORE_TOUCH_EVENTS" : "")
+ ((p.pokey & POKE_LOCK_SHORT_TIMEOUT) != 0
? " POKE_LOCK_SHORT_TIMEOUT" : "")
+ ((p.pokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0
@@ -1609,7 +1611,7 @@ class PowerManagerService extends IPowerManager.Stub
if (err == 0) {
mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0);
if (mUseSoftwareAutoBrightness) {
- enableLightSensor(on);
+ enableLightSensorLocked(on);
if (!on) {
// make sure button and key backlights are off too
mButtonLight.turnOff();
@@ -1742,6 +1744,8 @@ class PowerManagerService extends IPowerManager.Stub
} else {
// cancel light sensor task
mHandler.removeCallbacks(mAutoBrightnessTask);
+ mLightSensorPendingDecrease = false;
+ mLightSensorPendingIncrease = false;
mScreenOffTime = SystemClock.elapsedRealtime();
long identity = Binder.clearCallingIdentity();
try {
@@ -2212,31 +2216,13 @@ class PowerManagerService extends IPowerManager.Stub
private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
int eventType, boolean force) {
- if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)
- && (eventType == CHEEK_EVENT)) {
- if (false) {
- Slog.d(TAG, "dropping cheek event mPokey=0x" + Integer.toHexString(mPokey));
- }
- return;
- }
-
- if (((mPokey & POKE_LOCK_IGNORE_TOUCH_AND_CHEEK_EVENTS) != 0)
- && (eventType == TOUCH_EVENT || eventType == TOUCH_UP_EVENT
- || eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT)) {
+ if (((mPokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0) && (eventType == TOUCH_EVENT)) {
if (false) {
Slog.d(TAG, "dropping touch mPokey=0x" + Integer.toHexString(mPokey));
}
return;
}
- if (false) {
- if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)) {
- Slog.d(TAG, "userActivity !!!");//, new RuntimeException());
- } else {
- Slog.d(TAG, "mPokey=0x" + Integer.toHexString(mPokey));
- }
- }
-
synchronized (mLocks) {
if (mSpew) {
Slog.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time
@@ -2325,9 +2311,10 @@ class PowerManagerService extends IPowerManager.Stub
private Runnable mAutoBrightnessTask = new Runnable() {
public void run() {
synchronized (mLocks) {
- int value = (int)mLightSensorPendingValue;
- if (value >= 0) {
- mLightSensorPendingValue = -1;
+ if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
+ int value = (int)mLightSensorPendingValue;
+ mLightSensorPendingDecrease = false;
+ mLightSensorPendingIncrease = false;
lightSensorChangedLocked(value);
}
}
@@ -2569,16 +2556,12 @@ class PowerManagerService extends IPowerManager.Stub
}
private void setScreenBrightnessMode(int mode) {
- boolean enabled = (mode == SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
- if (mUseSoftwareAutoBrightness && mAutoBrightessEnabled != enabled) {
- mAutoBrightessEnabled = enabled;
- if (isScreenOn()) {
- // force recompute of backlight values
- if (mLightSensorValue >= 0) {
- int value = (int)mLightSensorValue;
- mLightSensorValue = -1;
- lightSensorChangedLocked(value);
- }
+ synchronized (mLocks) {
+ boolean enabled = (mode == SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ if (mUseSoftwareAutoBrightness && mAutoBrightessEnabled != enabled) {
+ mAutoBrightessEnabled = enabled;
+ // This will get us a new value
+ enableLightSensorLocked(mAutoBrightessEnabled && isScreenOn());
}
}
}
@@ -2604,7 +2587,8 @@ class PowerManagerService extends IPowerManager.Stub
}
mKeylightDelay = LONG_KEYLIGHT_DELAY;
if (totalDelay < 0) {
- mScreenOffDelay = Integer.MAX_VALUE;
+ // negative number means stay on as long as possible.
+ mScreenOffDelay = mMaximumScreenOffTimeout;
} else if (mKeylightDelay < totalDelay) {
// subtract the time that the keylight delay. This will give us the
// remainder of the time that we need to sleep to get the accurate
@@ -2729,7 +2713,6 @@ class PowerManagerService extends IPowerManager.Stub
// don't bother with the light sensor if auto brightness is handled in hardware
if (mUseSoftwareAutoBrightness) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
- enableLightSensor(true);
}
// wait until sensors are enabled before turning on screen.
@@ -2747,6 +2730,8 @@ class PowerManagerService extends IPowerManager.Stub
Slog.d(TAG, "system ready!");
mDoneBooting = true;
+ enableLightSensorLocked(mUseSoftwareAutoBrightness && mAutoBrightessEnabled);
+
long identity = Binder.clearCallingIdentity();
try {
mBatteryStats.noteScreenBrightness(getPreferredBrightness());
@@ -2901,9 +2886,13 @@ class PowerManagerService extends IPowerManager.Stub
}
}
- private void enableLightSensor(boolean enable) {
+ private void enableLightSensorLocked(boolean enable) {
if (mDebugLightSensor) {
- Slog.d(TAG, "enableLightSensor " + enable);
+ Slog.d(TAG, "enableLightSensorLocked enable=" + enable
+ + " mAutoBrightessEnabled=" + mAutoBrightessEnabled);
+ }
+ if (!mAutoBrightessEnabled) {
+ enable = false;
}
if (mSensorManager != null && mLightSensorEnabled != enable) {
mLightSensorEnabled = enable;
@@ -2980,19 +2969,29 @@ class PowerManagerService extends IPowerManager.Stub
if (mDebugLightSensor) {
Slog.d(TAG, "onSensorChanged: light value: " + value);
}
- mHandler.removeCallbacks(mAutoBrightnessTask);
- if (mLightSensorValue != value) {
- if (mLightSensorValue == -1 ||
- milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
- // process the value immediately if screen has just turned on
- lightSensorChangedLocked(value);
- } else {
+ if (mLightSensorValue == -1 ||
+ milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
+ // process the value immediately if screen has just turned on
+ mHandler.removeCallbacks(mAutoBrightnessTask);
+ mLightSensorPendingDecrease = false;
+ mLightSensorPendingIncrease = false;
+ lightSensorChangedLocked(value);
+ } else {
+ if ((value > mLightSensorValue && mLightSensorPendingDecrease) ||
+ (value < mLightSensorValue && mLightSensorPendingIncrease) ||
+ (value == mLightSensorValue) ||
+ (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) {
// delay processing to debounce the sensor
+ mHandler.removeCallbacks(mAutoBrightnessTask);
+ mLightSensorPendingDecrease = (value < mLightSensorValue);
+ mLightSensorPendingIncrease = (value > mLightSensorValue);
+ if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
+ mLightSensorPendingValue = value;
+ mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
+ }
+ } else {
mLightSensorPendingValue = value;
- mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
}
- } else {
- mLightSensorPendingValue = -1;
}
}
}
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 020f9ed..1a12a84 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -799,8 +799,9 @@ public class ProcessStats {
}
private String readFile(String file, char endChar) {
+ FileInputStream is = null;
try {
- FileInputStream is = new FileInputStream(file);
+ is = new FileInputStream(file);
int len = is.read(mBuffer);
is.close();
@@ -811,10 +812,17 @@ public class ProcessStats {
break;
}
}
- return new String(mBuffer, 0, 0, i);
+ return new String(mBuffer, 0, i);
}
} catch (java.io.FileNotFoundException e) {
} catch (java.io.IOException e) {
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (java.io.IOException e) {
+ }
+ }
}
return null;
}
@@ -841,4 +849,3 @@ public class ProcessStats {
}
}
}
-
diff --git a/services/java/com/android/server/SamplingProfilerService.java b/services/java/com/android/server/SamplingProfilerService.java
new file mode 100644
index 0000000..61267d0
--- /dev/null
+++ b/services/java/com/android/server/SamplingProfilerService.java
@@ -0,0 +1,117 @@
+/*
+ * 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;
+
+import android.content.ContentResolver;
+import android.os.DropBoxManager;
+import android.os.FileObserver;
+import android.os.Binder;
+
+import android.util.Slog;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import com.android.internal.os.SamplingProfilerIntegration;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class SamplingProfilerService extends Binder {
+
+ private static final String TAG = "SamplingProfilerService";
+ private static final boolean LOCAL_LOGV = false;
+ public static final String SNAPSHOT_DIR = SamplingProfilerIntegration.SNAPSHOT_DIR;
+
+ private FileObserver snapshotObserver;
+
+ public SamplingProfilerService(Context context) {
+ registerSettingObserver(context);
+ startWorking(context);
+ }
+
+ private void startWorking(Context context) {
+ if (LOCAL_LOGV) Slog.v(TAG, "starting SamplingProfilerService!");
+
+ final DropBoxManager dropbox =
+ (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);
+
+ // before FileObserver is ready, there could have already been some snapshots
+ // in the directory, we don't want to miss them
+ File[] snapshotFiles = new File(SNAPSHOT_DIR).listFiles();
+ for (int i = 0; snapshotFiles != null && i < snapshotFiles.length; i++) {
+ handleSnapshotFile(snapshotFiles[i], dropbox);
+ }
+
+ // detect new snapshot and put it in dropbox
+ // delete it afterwards no matter what happened before
+ // Note: needs listening at event ATTRIB rather than CLOSE_WRITE, because we set the
+ // readability of snapshot files after writing them!
+ snapshotObserver = new FileObserver(SNAPSHOT_DIR, FileObserver.ATTRIB) {
+ @Override
+ public void onEvent(int event, String path) {
+ handleSnapshotFile(new File(SNAPSHOT_DIR, path), dropbox);
+ }
+ };
+ snapshotObserver.startWatching();
+
+ if (LOCAL_LOGV) Slog.v(TAG, "SamplingProfilerService activated");
+ }
+
+ private void handleSnapshotFile(File file, DropBoxManager dropbox) {
+ try {
+ dropbox.addFile(TAG, file, 0);
+ if (LOCAL_LOGV) Slog.v(TAG, file.getPath() + " added to dropbox");
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't add " + file.getPath() + " to dropbox", e);
+ } finally {
+ file.delete();
+ }
+ }
+
+ private void registerSettingObserver(Context context) {
+ ContentResolver contentResolver = context.getContentResolver();
+ contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.SAMPLING_PROFILER_MS),
+ false, new SamplingProfilerSettingsObserver(contentResolver));
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("SamplingProfilerService:");
+ pw.println("Watching directory: " + SNAPSHOT_DIR);
+ }
+
+ private class SamplingProfilerSettingsObserver extends ContentObserver {
+ private ContentResolver mContentResolver;
+ public SamplingProfilerSettingsObserver(ContentResolver contentResolver) {
+ super(null);
+ mContentResolver = contentResolver;
+ onChange(false);
+ }
+ @Override
+ public void onChange(boolean selfChange) {
+ Integer samplingProfilerMs = Settings.Secure.getInt(
+ mContentResolver, Settings.Secure.SAMPLING_PROFILER_MS, 0);
+ // setting this secure property will start or stop sampling profiler,
+ // as well as adjust the the time between taking snapshots.
+ SystemProperties.set("persist.sys.profiler_ms", samplingProfilerMs.toString());
+ }
+ }
+}
diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/ScreenRotationAnimation.java
new file mode 100644
index 0000000..bef64b3
--- /dev/null
+++ b/services/java/com/android/server/ScreenRotationAnimation.java
@@ -0,0 +1,358 @@
+/*
+ * 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; // TODO: use com.android.server.wm, once things move there
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceSession;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Transformation;
+
+class ScreenRotationAnimation {
+ static final String TAG = "ScreenRotationAnimation";
+ static final boolean DEBUG = false;
+
+ final Context mContext;
+ final Display mDisplay;
+ Surface mSurface;
+ Surface mBlackSurface;
+ int mWidth, mHeight;
+
+ int mSnapshotRotation;
+ int mSnapshotDeltaRotation;
+ int mOriginalRotation;
+ int mOriginalWidth, mOriginalHeight;
+ int mCurRotation;
+
+ Animation mExitAnimation;
+ final Transformation mExitTransformation = new Transformation();
+ Animation mEnterAnimation;
+ final Transformation mEnterTransformation = new Transformation();
+ boolean mStarted;
+
+ final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ final Matrix mSnapshotInitialMatrix = new Matrix();
+ final Matrix mSnapshotFinalMatrix = new Matrix();
+ final float[] mTmpFloats = new float[9];
+
+ public ScreenRotationAnimation(Context context, Display display, SurfaceSession session,
+ boolean inTransaction) {
+ mContext = context;
+ mDisplay = display;
+
+ display.getMetrics(mDisplayMetrics);
+
+ Bitmap screenshot = Surface.screenshot(0, 0);
+
+ if (screenshot != null) {
+ // Screenshot does NOT include rotation!
+ mSnapshotRotation = 0;
+ mWidth = screenshot.getWidth();
+ mHeight = screenshot.getHeight();
+ } else {
+ // Just in case.
+ mSnapshotRotation = display.getRotation();
+ mWidth = mDisplayMetrics.widthPixels;
+ mHeight = mDisplayMetrics.heightPixels;
+ }
+
+ mOriginalRotation = display.getRotation();
+ mOriginalWidth = mDisplayMetrics.widthPixels;
+ mOriginalHeight = mDisplayMetrics.heightPixels;
+
+ if (!inTransaction) {
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+ ">>> OPEN TRANSACTION ScreenRotationAnimation");
+ Surface.openTransaction();
+ }
+
+ try {
+ try {
+ mSurface = new Surface(session, 0, "FreezeSurface",
+ -1, mWidth, mHeight, PixelFormat.OPAQUE, 0);
+ mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 200);
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.w(TAG, "Unable to allocate freeze surface", e);
+ }
+
+ if (false) {
+ try {
+ int size = mOriginalWidth > mOriginalHeight ? mOriginalWidth : mOriginalHeight;
+ mBlackSurface = new Surface(session, 0, "BlackSurface",
+ -1, size, size, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
+ mBlackSurface.setAlpha(1.0f);
+ mBlackSurface.setLayer(0);
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.w(TAG, "Unable to allocate black surface", e);
+ }
+ }
+
+ setRotation(display.getRotation());
+
+ if (mSurface != null) {
+ Rect dirty = new Rect(0, 0, mWidth, mHeight);
+ Canvas c = null;
+ try {
+ c = mSurface.lockCanvas(dirty);
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Unable to lock surface", e);
+ return;
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.w(TAG, "Unable to lock surface", e);
+ return;
+ }
+ if (c == null) {
+ Slog.w(TAG, "Null surface");
+ return;
+ }
+
+ if (screenshot != null) {
+ Paint paint = new Paint(0);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+ c.drawBitmap(screenshot, 0, 0, paint);
+ } else {
+ c.drawColor(Color.GREEN, PorterDuff.Mode.SRC);
+ }
+
+ mSurface.unlockCanvasAndPost(c);
+ }
+ } finally {
+ if (!inTransaction) {
+ Surface.closeTransaction();
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+ "<<< CLOSE TRANSACTION ScreenRotationAnimation");
+ }
+
+ if (screenshot != null) {
+ screenshot.recycle();
+ }
+ }
+ }
+
+ static int deltaRotation(int oldRotation, int newRotation) {
+ int delta = newRotation - oldRotation;
+ if (delta < 0) delta += 4;
+ return delta;
+ }
+
+ void setSnapshotTransform(Matrix matrix, float alpha) {
+ if (mSurface != null) {
+ matrix.getValues(mTmpFloats);
+ mSurface.setPosition((int)mTmpFloats[Matrix.MTRANS_X],
+ (int)mTmpFloats[Matrix.MTRANS_Y]);
+ mSurface.setMatrix(
+ mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+ mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+ mSurface.setAlpha(alpha);
+ if (DEBUG) {
+ float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
+ float[] dstPnts = new float[4];
+ matrix.mapPoints(dstPnts, srcPnts);
+ Slog.i(TAG, "Original : (" + srcPnts[0] + "," + srcPnts[1]
+ + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")");
+ Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1]
+ + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")");
+ }
+ }
+ }
+
+ public static void createRotationMatrix(int rotation, int width, int height,
+ Matrix outMatrix) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ outMatrix.reset();
+ break;
+ case Surface.ROTATION_90:
+ outMatrix.setRotate(90, 0, 0);
+ outMatrix.postTranslate(height, 0);
+ break;
+ case Surface.ROTATION_180:
+ outMatrix.setRotate(180, 0, 0);
+ outMatrix.postTranslate(width, height);
+ break;
+ case Surface.ROTATION_270:
+ outMatrix.setRotate(270, 0, 0);
+ outMatrix.postTranslate(0, width);
+ break;
+ }
+ }
+
+ // Must be called while in a transaction.
+ public void setRotation(int rotation) {
+ mCurRotation = rotation;
+
+ // Compute the transformation matrix that must be applied
+ // to the snapshot to make it stay in the same original position
+ // with the current screen rotation.
+ int delta = deltaRotation(rotation, mSnapshotRotation);
+ createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
+
+ if (DEBUG) Slog.v(TAG, "**** ROTATION: " + delta);
+ setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
+ }
+
+ /**
+ * Returns true if animating.
+ */
+ public boolean dismiss(long maxAnimationDuration, float animationScale) {
+ // Figure out how the screen has moved from the original rotation.
+ int delta = deltaRotation(mCurRotation, mOriginalRotation);
+ if (false && delta == 0) {
+ // Nothing changed, just remove the snapshot.
+ if (mSurface != null) {
+ mSurface.destroy();
+ mSurface = null;
+ }
+ return false;
+ }
+
+ switch (delta) {
+ case Surface.ROTATION_0:
+ mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_0_exit);
+ mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_0_enter);
+ break;
+ case Surface.ROTATION_90:
+ mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_plus_90_exit);
+ mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_plus_90_enter);
+ break;
+ case Surface.ROTATION_180:
+ mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_180_exit);
+ mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_180_enter);
+ break;
+ case Surface.ROTATION_270:
+ mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_minus_90_exit);
+ mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_minus_90_enter);
+ break;
+ }
+
+ mDisplay.getMetrics(mDisplayMetrics);
+
+ // Initialize the animations. This is a hack, redefining what "parent"
+ // means to allow supplying the last and next size. In this definition
+ // "%p" is the original (let's call it "previous") size, and "%" is the
+ // screen's current/new size.
+ mEnterAnimation.initialize(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+ mOriginalWidth, mOriginalHeight);
+ mExitAnimation.initialize(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+ mOriginalWidth, mOriginalHeight);
+ mStarted = false;
+
+ mExitAnimation.restrictDuration(maxAnimationDuration);
+ mExitAnimation.scaleCurrentDuration(animationScale);
+ mEnterAnimation.restrictDuration(maxAnimationDuration);
+ mEnterAnimation.scaleCurrentDuration(animationScale);
+
+ return true;
+ }
+
+ public void kill() {
+ if (mSurface != null) {
+ mSurface.destroy();
+ mSurface = null;
+ }
+ if (mBlackSurface != null) {
+ mBlackSurface.destroy();
+ mBlackSurface = null;
+ }
+ if (mExitAnimation != null) {
+ mExitAnimation.cancel();
+ mExitAnimation = null;
+ }
+ if (mEnterAnimation != null) {
+ mEnterAnimation.cancel();
+ mEnterAnimation = null;
+ }
+ }
+
+ public boolean isAnimating() {
+ return mEnterAnimation != null || mExitAnimation != null;
+ }
+
+ public boolean stepAnimation(long now) {
+ if (mEnterAnimation == null && mExitAnimation == null) {
+ return false;
+ }
+
+ if (!mStarted) {
+ mEnterAnimation.setStartTime(now);
+ mExitAnimation.setStartTime(now);
+ mStarted = true;
+ }
+
+ mExitTransformation.clear();
+ boolean moreExit = false;
+ if (mExitAnimation != null) {
+ moreExit = mExitAnimation.getTransformation(now, mExitTransformation);
+ if (DEBUG) Slog.v(TAG, "Stepped exit: " + mExitTransformation);
+ if (!moreExit) {
+ if (DEBUG) Slog.v(TAG, "Exit animation done!");
+ mExitAnimation.cancel();
+ mExitAnimation = null;
+ mExitTransformation.clear();
+ if (mSurface != null) {
+ mSurface.destroy();
+ mSurface = null;
+ }
+ if (mBlackSurface != null) {
+ mBlackSurface.destroy();
+ mBlackSurface = null;
+ }
+ }
+ }
+
+ mEnterTransformation.clear();
+ boolean moreEnter = false;
+ if (mEnterAnimation != null) {
+ moreEnter = mEnterAnimation.getTransformation(now, mEnterTransformation);
+ if (!moreEnter) {
+ mEnterAnimation.cancel();
+ mEnterAnimation = null;
+ mEnterTransformation.clear();
+ }
+ }
+
+ mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
+ setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
+
+ return moreEnter || moreExit;
+ }
+
+ public Transformation getEnterTransformation() {
+ return mEnterTransformation;
+ }
+}
diff --git a/services/java/com/android/server/ShutdownActivity.java b/services/java/com/android/server/ShutdownActivity.java
index 64b9c5d..c9d4d01 100644
--- a/services/java/com/android/server/ShutdownActivity.java
+++ b/services/java/com/android/server/ShutdownActivity.java
@@ -27,19 +27,26 @@ import com.android.internal.app.ShutdownThread;
public class ShutdownActivity extends Activity {
private static final String TAG = "ShutdownActivity";
+ private boolean mReboot;
private boolean mConfirm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ Intent intent = getIntent();
+ mReboot = Intent.ACTION_REBOOT.equals(intent.getAction());
+ mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
Handler h = new Handler();
h.post(new Runnable() {
public void run() {
- ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
+ if (mReboot) {
+ ShutdownThread.reboot(ShutdownActivity.this, null, mConfirm);
+ } else {
+ ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
+ }
}
});
}
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index e5dfb18..50ea3fa 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -32,6 +32,7 @@ import android.os.Binder;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Slog;
+import android.view.View;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
@@ -68,6 +69,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub
ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
int mDisabled = 0;
+ Object mLock = new Object();
+ // We usually call it lights out mode, but double negatives are annoying
+ boolean mLightsOn = true;
+ boolean mMenuVisible = false;
+ boolean mIMEButtonVisible = false;
+ IBinder mIMEToken = null;
+
private class DisableRecord implements IBinder.DeathRecipient {
String pkg;
int what;
@@ -84,6 +92,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub
void onSetDisabled(int status);
void onClearAll();
void onNotificationClick(String pkg, String tag, int id);
+ void onNotificationClear(String pkg, String tag, int id);
void onPanelRevealed();
void onNotificationError(String pkg, String tag, int id,
int uid, int initialPid, String message);
@@ -104,22 +113,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub
}
// ================================================================================
- // Constructing the view
- // ================================================================================
-
- public void systemReady() {
- }
-
- public void systemReady2() {
- ComponentName cn = ComponentName.unflattenFromString(
- mContext.getString(com.android.internal.R.string.config_statusBarComponent));
- Intent intent = new Intent();
- intent.setComponent(cn);
- Slog.i(TAG, "Starting service: " + cn);
- mContext.startService(intent);
- }
-
- // ================================================================================
// From IStatusBarService
// ================================================================================
public void expand() {
@@ -240,6 +233,107 @@ public class StatusBarManagerService extends IStatusBarService.Stub
}
}
+ /**
+ * Hide or show the on-screen Menu key. Only call this from the window manager, typically in
+ * response to a window with FLAG_NEEDS_MENU_KEY set.
+ */
+ public void setMenuKeyVisible(final boolean visible) {
+ enforceStatusBar();
+
+ if (SPEW) Slog.d(TAG, (visible?"showing":"hiding") + " MENU key");
+
+ synchronized(mLock) {
+ if (mMenuVisible != visible) {
+ mMenuVisible = visible;
+ mHandler.post(new Runnable() {
+ public void run() {
+ if (mBar != null) {
+ try {
+ mBar.setMenuKeyVisible(visible);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+
+ public void setIMEButtonVisible(final IBinder token, final boolean visible) {
+ enforceStatusBar();
+
+ if (SPEW) Slog.d(TAG, (visible?"showing":"hiding") + " IME Button");
+
+ synchronized(mLock) {
+ // In case of IME change, we need to call up setIMEButtonVisible() regardless of
+ // mIMEButtonVisible because mIMEButtonVisible may not have been set to false when the
+ // previous IME was destroyed.
+ mIMEButtonVisible = visible;
+ mIMEToken = token;
+ mHandler.post(new Runnable() {
+ public void run() {
+ if (mBar != null) {
+ try {
+ mBar.setIMEButtonVisible(token, visible);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * This is used for the automatic version of lights-out mode. Only call this from
+ * the window manager.
+ *
+ * @see setLightsOn(boolean)
+ */
+ public void setActiveWindowIsFullscreen(boolean fullscreen) {
+ // We could get away with a separate permission here, but STATUS_BAR is
+ // signatureOrSystem which is probably good enough. There is no public API
+ // for this, so the question is a security issue, not an API compatibility issue.
+ enforceStatusBar();
+
+ synchronized (mLock) {
+ updateLightsOnLocked(!fullscreen);
+ }
+ }
+
+ /**
+ * This is used for the user-controlled version of lights-out mode. Only call this from
+ * the status bar itself.
+ *
+ * We have two different functions here, because I think we're going to want to
+ * tweak the behavior when the user keeps turning lights-out mode off and the
+ * app keeps trying to turn it on. For now they can just fight it out. Having
+ * these two separte inputs will allow us to keep that change local to here. --joeo
+ */
+ public void setSystemUiVisibility(int vis) {
+ enforceStatusBarService();
+
+ synchronized (mLock) {
+ final boolean lightsOn = (vis & View.STATUS_BAR_HIDDEN) == 0;
+ updateLightsOnLocked(lightsOn);
+ }
+ }
+
+ private void updateLightsOnLocked(final boolean lightsOn) {
+ if (mLightsOn != lightsOn) {
+ mLightsOn = lightsOn;
+ mHandler.post(new Runnable() {
+ public void run() {
+ if (mBar != null) {
+ try {
+ mBar.setLightsOn(lightsOn);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+ });
+ }
+ }
+
private void enforceStatusBar() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
"StatusBarManagerService");
@@ -260,7 +354,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub
// Callbacks from the status bar service.
// ================================================================================
public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
- List<IBinder> notificationKeys, List<StatusBarNotification> notifications) {
+ List<IBinder> notificationKeys, List<StatusBarNotification> notifications,
+ int switches[], List<IBinder> binders) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
@@ -274,6 +369,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub
notifications.add(e.getValue());
}
}
+ synchronized (mLock) {
+ switches[0] = gatherDisableActionsLocked();
+ switches[1] = mLightsOn ? 1 : 0;
+ switches[2] = mMenuVisible ? 1 : 0;
+ switches[3] = mIMEButtonVisible ? 1 : 0;
+ binders.add(mIMEToken);
+ }
}
/**
@@ -301,6 +403,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub
mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message);
}
+ public void onNotificationClear(String pkg, String tag, int id) {
+ enforceStatusBarService();
+
+ mNotificationCallbacks.onNotificationClear(pkg, tag, id);
+ }
+
public void onClearAllNotifications() {
enforceStatusBarService();
diff --git a/services/java/com/android/server/StrictModeFlash.java b/services/java/com/android/server/StrictModeFlash.java
new file mode 100644
index 0000000..0a6c625
--- /dev/null
+++ b/services/java/com/android/server/StrictModeFlash.java
@@ -0,0 +1,114 @@
+/*
+ * 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; // TODO: use com.android.server.wm, once things move there
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceSession;
+
+class StrictModeFlash {
+ private static final String TAG = "StrictModeFlash";
+
+ Surface mSurface;
+ int mLastDW;
+ int mLastDH;
+ boolean mDrawNeeded;
+ final int mThickness = 20;
+
+ public StrictModeFlash(Display display, SurfaceSession session) {
+ final DisplayMetrics dm = new DisplayMetrics();
+ display.getMetrics(dm);
+
+ try {
+ mSurface = new Surface(session, 0, "StrictModeFlash", -1, 1, 1, PixelFormat.TRANSLUCENT, 0);
+ } catch (Surface.OutOfResourcesException e) {
+ return;
+ }
+
+ mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
+ mSurface.setPosition(0, 0);
+ mDrawNeeded = true;
+ }
+
+ private void drawIfNeeded() {
+ if (!mDrawNeeded) {
+ return;
+ }
+ mDrawNeeded = false;
+ final int dw = mLastDW;
+ final int dh = mLastDH;
+
+ Rect dirty = new Rect(0, 0, dw, dh);
+ Canvas c = null;
+ try {
+ c = mSurface.lockCanvas(dirty);
+ } catch (IllegalArgumentException e) {
+ } catch (Surface.OutOfResourcesException e) {
+ }
+ if (c == null) {
+ return;
+ }
+
+ // Top
+ c.clipRect(new Rect(0, 0, dw, mThickness), Region.Op.REPLACE);
+ c.drawColor(Color.RED);
+ // Left
+ c.clipRect(new Rect(0, 0, mThickness, dh), Region.Op.REPLACE);
+ c.drawColor(Color.RED);
+ // Right
+ c.clipRect(new Rect(dw - mThickness, 0, dw, dh), Region.Op.REPLACE);
+ c.drawColor(Color.RED);
+ // Bottom
+ c.clipRect(new Rect(0, dh - mThickness, dw, dh), Region.Op.REPLACE);
+ c.drawColor(Color.RED);
+
+ mSurface.unlockCanvasAndPost(c);
+ }
+
+ // Note: caller responsible for being inside
+ // Surface.openTransaction() / closeTransaction()
+ public void setVisibility(boolean on) {
+ if (mSurface == null) {
+ return;
+ }
+ drawIfNeeded();
+ if (on) {
+ mSurface.show();
+ } else {
+ mSurface.hide();
+ }
+ }
+
+ void positionSurface(int dw, int dh) {
+ if (mLastDW == dw && mLastDH == dh) {
+ return;
+ }
+ mLastDW = dw;
+ mLastDH = dh;
+ mSurface.setSize(dw, dh);
+ mDrawNeeded = true;
+ }
+
+}
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index fff1874..a1f43b4 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -16,18 +16,16 @@
package com.android.server;
-import android.app.backup.AbsoluteFileBackupHelper;
import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataInputStream;
import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupHelper;
import android.app.backup.BackupAgentHelper;
+import android.app.backup.WallpaperBackupHelper;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.os.ServiceManager;
-import android.os.SystemService;
import android.util.Slog;
+
import java.io.File;
import java.io.IOException;
@@ -54,7 +52,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
// TODO: Send a delete for any stored wallpaper image in this case?
files = new String[] { WALLPAPER_INFO };
}
- addHelper("wallpaper", new AbsoluteFileBackupHelper(SystemBackupAgent.this, files));
+ addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files));
super.onBackup(oldState, data, newState);
}
@@ -62,12 +60,11 @@ public class SystemBackupAgent extends BackupAgentHelper {
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
// On restore, we also support a previous data schema "system_files"
- addHelper("wallpaper", new AbsoluteFileBackupHelper(SystemBackupAgent.this,
+ addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this,
new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }));
- addHelper("system_files", new AbsoluteFileBackupHelper(SystemBackupAgent.this,
+ addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this,
new String[] { WALLPAPER_IMAGE }));
- boolean success = false;
try {
super.onRestore(data, appVersionCode, newState);
@@ -75,7 +72,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
Context.WALLPAPER_SERVICE);
wallpaper.settingsRestored();
} catch (IOException ex) {
- // If there was a failure, delete everything for the wallpaper, this is too aggresive,
+ // If there was a failure, delete everything for the wallpaper, this is too aggressive,
// but this is hopefully a rare failure.
Slog.d(TAG, "restore failed", ex);
(new File(WALLPAPER_IMAGE)).delete();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index df69b76..978946f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -24,6 +24,7 @@ import com.android.internal.os.SamplingProfilerIntegration;
import dalvik.system.VMRuntime;
import dalvik.system.Zygote;
+import android.accounts.AccountManagerService;
import android.app.ActivityManagerNative;
import android.bluetooth.BluetoothAdapter;
import android.content.ComponentName;
@@ -32,9 +33,10 @@ import android.content.ContentService;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
+import android.content.res.Configuration;
import android.database.ContentObserver;
-import android.database.Cursor;
import android.media.AudioService;
+import android.os.Build;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -46,10 +48,12 @@ import android.provider.Settings;
import android.server.BluetoothA2dpService;
import android.server.BluetoothService;
import android.server.search.SearchManagerService;
+import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
-import android.accounts.AccountManagerService;
+import android.view.Display;
+import android.view.WindowManager;
import java.io.File;
import java.util.Timer;
@@ -57,11 +61,8 @@ import java.util.TimerTask;
class ServerThread extends Thread {
private static final String TAG = "SystemServer";
- private final static boolean INCLUDE_DEMO = false;
- private static final int LOG_BOOT_PROGRESS_SYSTEM_RUN = 3010;
-
- private ContentResolver mContentResolver;
+ ContentResolver mContentResolver;
private class AdbSettingsObserver extends ContentObserver {
public AdbSettingsObserver() {
@@ -120,12 +121,13 @@ class ServerThread extends Thread {
WindowManagerService wm = null;
BluetoothService bluetooth = null;
BluetoothA2dpService bluetoothA2dp = null;
- HeadsetObserver headset = null;
+ WiredAccessoryObserver wiredAccessory = null;
DockObserver dock = null;
- UsbObserver usb = null;
+ UsbService usb = null;
UiModeManagerService uiMode = null;
RecognitionManagerService recognition = null;
ThrottleService throttle = null;
+ NetworkTimeUpdateService networkTimeUpdater = null;
// Critical services...
try {
@@ -168,13 +170,13 @@ class ServerThread extends Thread {
Slog.i(TAG, "System Content Providers");
ActivityManagerService.installSystemProviders();
- Slog.i(TAG, "Battery Service");
- battery = new BatteryService(context);
- ServiceManager.addService("battery", battery);
-
Slog.i(TAG, "Lights Service");
lights = new LightsService(context);
+ Slog.i(TAG, "Battery Service");
+ battery = new BatteryService(context, lights);
+ ServiceManager.addService("battery", battery);
+
Slog.i(TAG, "Vibrator Service");
ServiceManager.addService("vibrator", new VibratorService(context));
@@ -202,11 +204,9 @@ class ServerThread extends Thread {
// TODO: Use a more reliable check to see if this product should
// support Bluetooth - see bug 988521
if (SystemProperties.get("ro.kernel.qemu").equals("1")) {
- Slog.i(TAG, "Registering null Bluetooth Service (emulator)");
- ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, null);
+ Slog.i(TAG, "No Bluetooh Service (emulator)");
} else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
- Slog.i(TAG, "Registering null Bluetooth Service (factory test)");
- ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, null);
+ Slog.i(TAG, "No Bluetooth Service (factory test)");
} else {
Slog.i(TAG, "Bluetooth Service");
bluetooth = new BluetoothService(context);
@@ -215,6 +215,7 @@ class ServerThread extends Thread {
bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
bluetoothA2dp);
+ bluetooth.initAfterA2dpRegistration();
int bluetoothOn = Settings.Secure.getInt(mContentResolver,
Settings.Secure.BLUETOOTH_ON, 0);
@@ -234,6 +235,7 @@ class ServerThread extends Thread {
NotificationManagerService notification = null;
WallpaperManagerService wallpaper = null;
LocationManagerService location = null;
+ CountryDetectorService countryDetector = null;
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
@@ -345,6 +347,14 @@ class ServerThread extends Thread {
}
try {
+ Slog.i(TAG, "Country Detector");
+ countryDetector = new CountryDetectorService(context);
+ ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Country Detector", e);
+ }
+
+ try {
Slog.i(TAG, "Search Service");
ServiceManager.addService(Context.SEARCH_SERVICE,
new SearchManagerService(context));
@@ -352,11 +362,6 @@ class ServerThread extends Thread {
Slog.e(TAG, "Failure starting Search Service", e);
}
- if (INCLUDE_DEMO) {
- Slog.i(TAG, "Installing demo data...");
- (new DemoThread(context)).start();
- }
-
try {
Slog.i(TAG, "DropBox Service");
ServiceManager.addService(Context.DROPBOX_SERVICE,
@@ -381,14 +386,6 @@ class ServerThread extends Thread {
}
try {
- Slog.i(TAG, "Headset Observer");
- // Listen for wired headset changes
- headset = new HeadsetObserver(context);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting HeadsetObserver", e);
- }
-
- try {
Slog.i(TAG, "Dock Observer");
// Listen for dock station changes
dock = new DockObserver(context, power);
@@ -397,11 +394,19 @@ class ServerThread extends Thread {
}
try {
+ Slog.i(TAG, "Wired Accessory Observer");
+ // Listen for wired headset changes
+ wiredAccessory = new WiredAccessoryObserver(context);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting WiredAccessoryObserver", e);
+ }
+
+ try {
Slog.i(TAG, "USB Observer");
// Listen for USB changes
- usb = new UsbObserver(context);
+ usb = new UsbService(context);
} catch (Throwable e) {
- Slog.e(TAG, "Failure starting UsbObserver", e);
+ Slog.e(TAG, "Failure starting UsbService", e);
}
try {
@@ -434,13 +439,32 @@ class ServerThread extends Thread {
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Recognition Service", e);
}
-
+
try {
Slog.i(TAG, "DiskStats Service");
ServiceManager.addService("diskstats", new DiskStatsService(context));
} catch (Throwable e) {
Slog.e(TAG, "Failure starting DiskStats Service", e);
}
+
+ try {
+ // need to add this service even if SamplingProfilerIntegration.isEnabled()
+ // is false, because it is this service that detects system property change and
+ // turns on SamplingProfilerIntegration. Plus, when sampling profiler doesn't work,
+ // there is little overhead for running this service.
+ Slog.i(TAG, "SamplingProfiler Service");
+ ServiceManager.addService("samplingprofiler",
+ new SamplingProfilerService(context));
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting SamplingProfiler Service", e);
+ }
+
+ try {
+ Slog.i(TAG, "NetworkTimeUpdateService");
+ networkTimeUpdater = new NetworkTimeUpdateService(context);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting NetworkTimeUpdate service");
+ }
}
// make sure the ADB_ENABLED setting value matches the secure property value
@@ -455,14 +479,11 @@ class ServerThread extends Thread {
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();
if (safeMode) {
- try {
- ActivityManagerNative.getDefault().enterSafeMode();
- // Post the safe mode state in the Zygote class
- Zygote.systemInSafeMode = true;
- // Disable the JIT for the system_server process
- VMRuntime.getRuntime().disableJitCompilation();
- } catch (RemoteException e) {
- }
+ ActivityManagerService.self().enterSafeMode();
+ // Post the safe mode state in the Zygote class
+ Zygote.systemInSafeMode = true;
+ // Disable the JIT for the system_server process
+ VMRuntime.getRuntime().disableJitCompilation();
} else {
// Enable the JIT for the system_server process
VMRuntime.getRuntime().startJitCompilation();
@@ -478,10 +499,21 @@ class ServerThread extends Thread {
notification.systemReady();
}
- if (statusBar != null) {
- statusBar.systemReady();
- }
wm.systemReady();
+
+ if (safeMode) {
+ ActivityManagerService.self().showSafeModeOverlay();
+ }
+
+ // Update the configuration for this context by hand, because we're going
+ // to start using it before the config change done in wm.systemReady() will
+ // propagate to it.
+ Configuration config = wm.computeNewConfiguration();
+ DisplayMetrics metrics = new DisplayMetrics();
+ WindowManager w = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+ w.getDefaultDisplay().getMetrics(metrics);
+ context.getResources().updateConfiguration(config, metrics);
+
power.systemReady();
try {
pm.systemReady();
@@ -489,11 +521,11 @@ class ServerThread extends Thread {
}
// These are needed to propagate to the runnable below.
- final StatusBarManagerService statusBarF = statusBar;
+ final Context contextF = context;
final BatteryService batteryF = battery;
final ConnectivityService connectivityF = connectivity;
final DockObserver dockF = dock;
- final UsbObserver usbF = usb;
+ final UsbService usbF = usb;
final ThrottleService throttleF = throttle;
final UiModeManagerService uiModeF = uiMode;
final AppWidgetService appWidgetF = appWidget;
@@ -501,6 +533,8 @@ class ServerThread extends Thread {
final InputMethodManagerService immF = imm;
final RecognitionManagerService recognitionF = recognition;
final LocationManagerService locationF = location;
+ final CountryDetectorService countryDetectorF = countryDetector;
+ final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -512,7 +546,7 @@ class ServerThread extends Thread {
public void run() {
Slog.i(TAG, "Making services ready");
- if (statusBarF != null) statusBarF.systemReady2();
+ startSystemUi(contextF);
if (batteryF != null) batteryF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
@@ -528,7 +562,9 @@ class ServerThread extends Thread {
if (wallpaperF != null) wallpaperF.systemReady();
if (immF != null) immF.systemReady();
if (locationF != null) locationF.systemReady();
+ if (countryDetectorF != null) countryDetectorF.systemReady();
if (throttleF != null) throttleF.systemReady();
+ if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady();
}
});
@@ -540,39 +576,17 @@ class ServerThread extends Thread {
Looper.loop();
Slog.d(TAG, "System ServerThread is exiting!");
}
-}
-class DemoThread extends Thread
-{
- DemoThread(Context context)
- {
- mContext = context;
+ static final void startSystemUi(Context context) {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName("com.android.systemui",
+ "com.android.systemui.SystemUIService"));
+ Slog.d(TAG, "Starting service: " + intent);
+ context.startService(intent);
}
-
- @Override
- public void run()
- {
- try {
- Cursor c = mContext.getContentResolver().query(People.CONTENT_URI, null, null, null, null);
- boolean hasData = c != null && c.moveToFirst();
- if (c != null) {
- c.deactivate();
- }
- if (!hasData) {
- DemoDataSet dataset = new DemoDataSet();
- dataset.add(mContext);
- }
- } catch (Throwable e) {
- Slog.e("SystemServer", "Failure installing demo data", e);
- }
-
- }
-
- Context mContext;
}
-public class SystemServer
-{
+public class SystemServer {
private static final String TAG = "SystemServer";
public static final int FACTORY_TEST_OFF = 0;
@@ -610,7 +624,7 @@ public class SystemServer
timer.schedule(new TimerTask() {
@Override
public void run() {
- SamplingProfilerIntegration.writeSnapshot("system_server");
+ SamplingProfilerIntegration.writeSnapshot("system_server", null);
}
}, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
}
@@ -618,7 +632,7 @@ public class SystemServer
// The system server has to run all of the time, so it needs to be
// as efficient as possible with its memory usage.
VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
-
+
System.loadLibrary("android_servers");
init1(args);
}
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 3370279..eb14180 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -19,7 +19,8 @@ package com.android.server;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.NetworkUtils;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -35,12 +36,14 @@ import android.util.Slog;
import java.util.ArrayList;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.net.NetworkInterface;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.DefaultPhoneNotifier;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyIntents;
import com.android.server.am.BatteryStatsService;
@@ -63,7 +66,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private final Context mContext;
- private final ArrayList<Record> mRecords = new ArrayList();
+ // access should be inside synchronized (mRecords) for these two fields
+ private final ArrayList<IBinder> mRemoveList = new ArrayList<IBinder>();
+ private final ArrayList<Record> mRecords = new ArrayList<Record>();
private final IBatteryStats mBatteryStats;
@@ -81,7 +86,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
- private int mDataConnectionState = TelephonyManager.DATA_CONNECTED;
+ private int mDataConnectionState = TelephonyManager.DATA_UNKNOWN;
private boolean mDataConnectionPossible = false;
@@ -89,14 +94,18 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private String mDataConnectionApn = "";
- private String[] mDataConnectionApnTypes = null;
+ private ArrayList<String> mConnectedApns;
- private String mDataConnectionInterfaceName = "";
+ private LinkProperties mDataConnectionLinkProperties;
+
+ private LinkCapabilities mDataConnectionLinkCapabilities;
private Bundle mCellLocation = new Bundle();
private int mDataConnectionNetworkType;
+ private int mOtaspMode = ServiceStateTracker.OTASP_UNKNOWN;
+
static final int PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
PhoneStateListener.LISTEN_CALL_STATE |
@@ -121,6 +130,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
mContext = context;
mBatteryStats = BatteryStatsService.getService();
+ mConnectedApns = new ArrayList<String>();
}
public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
@@ -153,7 +163,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.events = events;
if (notifyNow) {
if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
- sendServiceState(r, mServiceState);
+ try {
+ r.callback.onServiceStateChanged(new ServiceState(mServiceState));
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
}
if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
try {
@@ -179,7 +193,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
- sendCellLocation(r, mCellLocation);
+ try {
+ r.callback.onCellLocationChanged(new Bundle(mCellLocation));
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
}
if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
try {
@@ -210,6 +228,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) {
+ try {
+ r.callback.onOtaspChanged(mOtaspMode);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -236,16 +261,16 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
mCallState = state;
mCallIncomingNumber = incomingNumber;
- for (int i = mRecords.size() - 1; i >= 0; i--) {
- Record r = mRecords.get(i);
+ for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
try {
r.callback.onCallStateChanged(state, incomingNumber);
} catch (RemoteException ex) {
- remove(r.binder);
+ mRemoveList.add(r.binder);
}
}
}
+ handleRemoveListLocked();
}
broadcastCallStateChanged(state, incomingNumber);
}
@@ -257,12 +282,16 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
Slog.i(TAG, "notifyServiceState: " + state);
synchronized (mRecords) {
mServiceState = state;
- for (int i = mRecords.size() - 1; i >= 0; i--) {
- Record r = mRecords.get(i);
+ for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
- sendServiceState(r, state);
+ try {
+ r.callback.onServiceStateChanged(new ServiceState(state));
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
}
+ handleRemoveListLocked();
}
broadcastServiceStateChanged(state);
}
@@ -273,10 +302,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
synchronized (mRecords) {
mSignalStrength = signalStrength;
- for (int i = mRecords.size() - 1; i >= 0; i--) {
- Record r = mRecords.get(i);
+ for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
- sendSignalStrength(r, signalStrength);
+ try {
+ r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
try {
@@ -284,10 +316,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
: gsmSignalStrength));
} catch (RemoteException ex) {
- remove(r.binder);
+ mRemoveList.add(r.binder);
}
}
}
+ handleRemoveListLocked();
}
broadcastSignalStrengthChanged(signalStrength);
}
@@ -296,19 +329,18 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
return;
}
- Slog.i(TAG, "notifyMessageWaitingChanged: " + mwi);
synchronized (mRecords) {
mMessageWaiting = mwi;
- for (int i = mRecords.size() - 1; i >= 0; i--) {
- Record r = mRecords.get(i);
+ for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
try {
r.callback.onMessageWaitingIndicatorChanged(mwi);
} catch (RemoteException ex) {
- remove(r.binder);
+ mRemoveList.add(r.binder);
}
}
}
+ handleRemoveListLocked();
}
}
@@ -316,19 +348,18 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!checkNotifyPermission("notifyCallForwardingChanged()")) {
return;
}
- Slog.i(TAG, "notifyCallForwardingChanged: " + cfi);
synchronized (mRecords) {
mCallForwarding = cfi;
- for (int i = mRecords.size() - 1; i >= 0; i--) {
- Record r = mRecords.get(i);
+ for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
try {
r.callback.onCallForwardingIndicatorChanged(cfi);
} catch (RemoteException ex) {
- remove(r.binder);
+ mRemoveList.add(r.binder);
}
}
}
+ handleRemoveListLocked();
}
}
@@ -338,58 +369,83 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
synchronized (mRecords) {
mDataActivity = state;
- for (int i = mRecords.size() - 1; i >= 0; i--) {
- Record r = mRecords.get(i);
+ for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
try {
r.callback.onDataActivity(state);
} catch (RemoteException ex) {
- remove(r.binder);
+ mRemoveList.add(r.binder);
}
}
}
+ handleRemoveListLocked();
}
}
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String[] apnTypes, String interfaceName, int networkType,
- String gateway) {
+ String reason, String apn, String apnType, LinkProperties linkProperties,
+ LinkCapabilities linkCapabilities, int networkType) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
Slog.i(TAG, "notifyDataConnection: state=" + state + " isDataConnectivityPossible="
- + isDataConnectivityPossible + " reason=" + reason
- + " interfaceName=" + interfaceName + " networkType=" + networkType);
+ + isDataConnectivityPossible + " reason='" + reason
+ + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType);
synchronized (mRecords) {
- mDataConnectionState = state;
+ boolean modified = false;
+ if (state == TelephonyManager.DATA_CONNECTED) {
+ if (!mConnectedApns.contains(apnType)) {
+ mConnectedApns.add(apnType);
+ if (mDataConnectionState != state) {
+ mDataConnectionState = state;
+ modified = true;
+ }
+ }
+ } else {
+ if (mConnectedApns.remove(apnType)) {
+ if (mConnectedApns.isEmpty()) {
+ mDataConnectionState = state;
+ modified = true;
+ } else {
+ // leave mDataConnectionState as is and
+ // send out the new status for the APN in question.
+ }
+ }
+ }
mDataConnectionPossible = isDataConnectivityPossible;
mDataConnectionReason = reason;
- mDataConnectionApn = apn;
- mDataConnectionApnTypes = apnTypes;
- mDataConnectionInterfaceName = interfaceName;
- mDataConnectionNetworkType = networkType;
- for (int i = mRecords.size() - 1; i >= 0; i--) {
- Record r = mRecords.get(i);
- if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
- try {
- r.callback.onDataConnectionStateChanged(state, networkType);
- } catch (RemoteException ex) {
- remove(r.binder);
+ mDataConnectionLinkProperties = linkProperties;
+ mDataConnectionLinkCapabilities = linkCapabilities;
+ if (mDataConnectionNetworkType != networkType) {
+ mDataConnectionNetworkType = networkType;
+ // need to tell registered listeners about the new network type
+ modified = true;
+ }
+ if (modified) {
+ Slog.d(TAG, "onDataConnectionStateChanged(" + state + ", " + networkType + ")");
+ for (Record r : mRecords) {
+ if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
+ try {
+ r.callback.onDataConnectionStateChanged(state, networkType);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
}
+ handleRemoveListLocked();
}
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
- apnTypes, interfaceName, gateway);
+ apnType, linkProperties, linkCapabilities);
}
- public void notifyDataConnectionFailed(String reason) {
+ public void notifyDataConnectionFailed(String reason, String apnType) {
if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
return;
}
/*
- * This is commented out because there is on onDataConnectionFailed callback
- * on PhoneStateListener. There should be
+ * This is commented out because there is no onDataConnectionFailed callback
+ * in PhoneStateListener. There should be.
synchronized (mRecords) {
mDataConnectionFailedReason = reason;
final int N = mRecords.size();
@@ -401,7 +457,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
*/
- broadcastDataConnectionFailed(reason);
+ broadcastDataConnectionFailed(reason, apnType);
}
public void notifyCellLocation(Bundle cellLocation) {
@@ -410,39 +466,36 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
synchronized (mRecords) {
mCellLocation = cellLocation;
- for (int i = mRecords.size() - 1; i >= 0; i--) {
- Record r = mRecords.get(i);
+ for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
- sendCellLocation(r, cellLocation);
+ try {
+ r.callback.onCellLocationChanged(new Bundle(cellLocation));
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+
}
}
+ handleRemoveListLocked();
}
}
- /**
- * Copy the service state object so they can't mess it up in the local calls
- */
- public void sendServiceState(Record r, ServiceState state) {
- try {
- r.callback.onServiceStateChanged(new ServiceState(state));
- } catch (RemoteException ex) {
- remove(r.binder);
- }
- }
-
- private void sendCellLocation(Record r, Bundle cellLocation) {
- try {
- r.callback.onCellLocationChanged(new Bundle(cellLocation));
- } catch (RemoteException ex) {
- remove(r.binder);
+ public void notifyOtaspChanged(int otaspMode) {
+ if (!checkNotifyPermission("notifyOtaspChanged()" )) {
+ return;
}
- }
-
- private void sendSignalStrength(Record r, SignalStrength signalStrength) {
- try {
- r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
- } catch (RemoteException ex) {
- remove(r.binder);
+ synchronized (mRecords) {
+ mOtaspMode = otaspMode;
+ for (Record r : mRecords) {
+ if ((r.events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) {
+ try {
+ r.callback.onOtaspChanged(otaspMode);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
}
}
@@ -468,11 +521,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println(" mDataConnectionPossible=" + mDataConnectionPossible);
pw.println(" mDataConnectionReason=" + mDataConnectionReason);
pw.println(" mDataConnectionApn=" + mDataConnectionApn);
- pw.println(" mDataConnectionInterfaceName=" + mDataConnectionInterfaceName);
+ pw.println(" mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
+ pw.println(" mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities);
pw.println(" mCellLocation=" + mCellLocation);
pw.println("registrations: count=" + recordCount);
- for (int i = 0; i < recordCount; i++) {
- Record r = mRecords.get(i);
+ for (Record r : mRecords) {
pw.println(" " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events));
}
}
@@ -543,7 +596,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private void broadcastDataConnectionStateChanged(int state,
boolean isDataConnectivityPossible,
- String reason, String apn, String[] apnTypes, String interfaceName, String gateway) {
+ String reason, String apn, String apnType, LinkProperties linkProperties,
+ LinkCapabilities linkCapabilities) {
// Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the
// required info.
@@ -556,29 +610,26 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (reason != null) {
intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
}
- intent.putExtra(Phone.DATA_APN_KEY, apn);
- String types = new String("");
- if (apnTypes.length > 0) {
- types = apnTypes[0];
- for (int i = 1; i < apnTypes.length; i++) {
- types = types+","+apnTypes[i];
+ if (linkProperties != null) {
+ intent.putExtra(Phone.DATA_LINK_PROPERTIES_KEY, linkProperties);
+ String iface = linkProperties.getInterfaceName();
+ if (iface != null) {
+ intent.putExtra(Phone.DATA_IFACE_NAME_KEY, iface);
}
}
- intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
- intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
- int gatewayAddr = 0;
- if (gateway != null) {
- gatewayAddr = NetworkUtils.v4StringToInt(gateway);
+ if (linkCapabilities != null) {
+ intent.putExtra(Phone.DATA_LINK_CAPABILITIES_KEY, linkCapabilities);
}
- intent.putExtra(Phone.DATA_GATEWAY_KEY, gatewayAddr);
-
+ intent.putExtra(Phone.DATA_APN_KEY, apn);
+ intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
mContext.sendStickyBroadcast(intent);
}
- private void broadcastDataConnectionFailed(String reason) {
+ private void broadcastDataConnectionFailed(String reason, String apnType) {
Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(Phone.FAILURE_REASON_KEY, reason);
+ intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
mContext.sendStickyBroadcast(intent);
}
@@ -605,4 +656,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
android.Manifest.permission.READ_PHONE_STATE, null);
}
}
+
+ private void handleRemoveListLocked() {
+ if (mRemoveList.size() > 0) {
+ for (IBinder b: mRemoveList) {
+ remove(b);
+ }
+ mRemoveList.clear();
+ }
+ }
}
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index a93a6ee..d841cb3 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -60,6 +60,8 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.GregorianCalendar;
import java.util.Properties;
import java.util.Random;
@@ -83,8 +85,8 @@ public class ThrottleService extends IThrottleManager.Stub {
private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
private int mPolicyPollPeriodSec;
- private long mPolicyThreshold;
- private int mPolicyThrottleValue;
+ private AtomicLong mPolicyThreshold;
+ private AtomicInteger mPolicyThrottleValue;
private int mPolicyResetDay; // 1-28
private int mPolicyNotificationsAllowedMask;
@@ -114,7 +116,7 @@ public class ThrottleService extends IThrottleManager.Stub {
private InterfaceObserver mInterfaceObserver;
private SettingsObserver mSettingsObserver;
- private int mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
+ private AtomicInteger mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
@@ -126,6 +128,10 @@ public class ThrottleService extends IThrottleManager.Stub {
if (VDBG) Slog.v(TAG, "Starting ThrottleService");
mContext = context;
+ mPolicyThreshold = new AtomicLong();
+ mPolicyThrottleValue = new AtomicInteger();
+ mThrottleIndex = new AtomicInteger();
+
mNtpActive = false;
mIface = mContext.getResources().getString(R.string.config_datause_iface);
@@ -214,7 +220,7 @@ public class ThrottleService extends IThrottleManager.Stub {
}
private long ntpToWallTime(long ntpTime) {
- long bestNow = getBestTime();
+ long bestNow = getBestTime(true); // do it quickly
long localNow = System.currentTimeMillis();
return localNow + (ntpTime - bestNow);
}
@@ -222,40 +228,42 @@ public class ThrottleService extends IThrottleManager.Stub {
// TODO - fetch for the iface
// return time in the local, system wall time, correcting for the use of ntp
- public synchronized long getResetTime(String iface) {
+ public long getResetTime(String iface) {
enforceAccessPermission();
long resetTime = 0;
if (mRecorder != null) {
- resetTime = ntpToWallTime(mRecorder.getPeriodEnd());
+ resetTime = mRecorder.getPeriodEnd();
}
+ resetTime = ntpToWallTime(resetTime);
return resetTime;
}
// TODO - fetch for the iface
// return time in the local, system wall time, correcting for the use of ntp
- public synchronized long getPeriodStartTime(String iface) {
- enforceAccessPermission();
+ public long getPeriodStartTime(String iface) {
long startTime = 0;
+ enforceAccessPermission();
if (mRecorder != null) {
- startTime = ntpToWallTime(mRecorder.getPeriodStart());
+ startTime = mRecorder.getPeriodStart();
}
+ startTime = ntpToWallTime(startTime);
return startTime;
}
//TODO - a better name? getCliffByteCountThreshold?
// TODO - fetch for the iface
- public synchronized long getCliffThreshold(String iface, int cliff) {
+ public long getCliffThreshold(String iface, int cliff) {
enforceAccessPermission();
if (cliff == 1) {
- return mPolicyThreshold;
+ return mPolicyThreshold.get();
}
return 0;
}
// TODO - a better name? getThrottleRate?
// TODO - fetch for the iface
- public synchronized int getCliffLevel(String iface, int cliff) {
+ public int getCliffLevel(String iface, int cliff) {
enforceAccessPermission();
if (cliff == 1) {
- return mPolicyThrottleValue;
+ return mPolicyThrottleValue.get();
}
return 0;
}
@@ -267,10 +275,9 @@ public class ThrottleService extends IThrottleManager.Stub {
}
// TODO - fetch for the iface
- public synchronized long getByteCount(String iface, int dir, int period, int ago) {
+ public long getByteCount(String iface, int dir, int period, int ago) {
enforceAccessPermission();
- if ((period == ThrottleManager.PERIOD_CYCLE) &&
- (mRecorder != null)) {
+ if ((period == ThrottleManager.PERIOD_CYCLE) && (mRecorder != null)) {
if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
}
@@ -279,10 +286,10 @@ public class ThrottleService extends IThrottleManager.Stub {
// TODO - a better name - getCurrentThrottleRate?
// TODO - fetch for the iface
- public synchronized int getThrottle(String iface) {
+ public int getThrottle(String iface) {
enforceAccessPermission();
- if (mThrottleIndex == 1) {
- return mPolicyThrottleValue;
+ if (mThrottleIndex.get() == 1) {
+ return mPolicyThrottleValue.get();
}
return 0;
}
@@ -305,22 +312,6 @@ public class ThrottleService extends IThrottleManager.Stub {
}
}, new IntentFilter(ACTION_RESET));
- // use a new thread as we don't want to stall the system for file writes
- mThread = new HandlerThread(TAG);
- mThread.start();
- mHandler = new MyHandler(mThread.getLooper());
- mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
-
- mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
- try {
- mNMService.registerObserver(mInterfaceObserver);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not register InterfaceObserver " + e);
- }
-
- mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
- mSettingsObserver.observe(mContext);
-
FileInputStream stream = null;
try {
Properties properties = new Properties();
@@ -337,6 +328,22 @@ public class ThrottleService extends IThrottleManager.Stub {
} catch (Exception e) {}
}
}
+
+ // use a new thread as we don't want to stall the system for file writes
+ mThread = new HandlerThread(TAG);
+ mThread.start();
+ mHandler = new MyHandler(mThread.getLooper());
+ mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
+
+ mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
+ try {
+ mNMService.registerObserver(mInterfaceObserver);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not register InterfaceObserver " + e);
+ }
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
+ mSettingsObserver.observe(mContext);
}
@@ -375,7 +382,7 @@ public class ThrottleService extends IThrottleManager.Stub {
// check for sim change TODO
// reregister for notification of policy change
- mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED;
+ mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
mRecorder = new DataRecorder(mContext, ThrottleService.this);
@@ -403,15 +410,16 @@ public class ThrottleService extends IThrottleManager.Stub {
R.integer.config_datause_threshold_bytes);
int defaultValue = mContext.getResources().getInteger(
R.integer.config_datause_throttle_kbitsps);
- synchronized (ThrottleService.this) {
- mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(),
- Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
- mPolicyThrottleValue = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
- if (testing) {
- mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
- mPolicyThreshold = TESTING_THRESHOLD;
- }
+ long threshold = Settings.Secure.getLong(mContext.getContentResolver(),
+ Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
+ int value = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
+
+ mPolicyThreshold.set(threshold);
+ mPolicyThrottleValue.set(value);
+ if (testing) {
+ mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
+ mPolicyThreshold.set(TESTING_THRESHOLD);
}
mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
@@ -423,10 +431,8 @@ public class ThrottleService extends IThrottleManager.Stub {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
}
- synchronized (ThrottleService.this) {
- if (mIface == null) {
- mPolicyThreshold = 0;
- }
+ if (mIface == null) {
+ mPolicyThreshold.set(0);
}
int defaultNotificationType = mContext.getResources().getInteger(
@@ -437,15 +443,16 @@ public class ThrottleService extends IThrottleManager.Stub {
mMaxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC, MAX_NTP_CACHE_AGE_SEC);
- if (VDBG || (mPolicyThreshold != 0)) {
+ if (VDBG || (mPolicyThreshold.get() != 0)) {
Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
- mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold + ", value=" +
- mPolicyThrottleValue + ", resetDay=" + mPolicyResetDay + ", noteType=" +
- mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" + mMaxNtpCacheAgeSec);
+ mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() +
+ ", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay +
+ ", noteType=" + mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" +
+ mMaxNtpCacheAgeSec);
}
// force updates
- mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED;
+ mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
onResetAlarm();
@@ -487,7 +494,7 @@ public class ThrottleService extends IThrottleManager.Stub {
long periodRx = mRecorder.getPeriodRx(0);
long periodTx = mRecorder.getPeriodTx(0);
long total = periodRx + periodTx;
- if (VDBG || (mPolicyThreshold != 0)) {
+ if (VDBG || (mPolicyThreshold.get() != 0)) {
Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
}
@@ -510,11 +517,11 @@ public class ThrottleService extends IThrottleManager.Stub {
private void onIfaceUp() {
// if we were throttled before, be sure and set it again - the iface went down
// (and may have disappeared all together) and these settings were lost
- if (mThrottleIndex == 1) {
+ if (mThrottleIndex.get() == 1) {
try {
mNMService.setInterfaceThrottle(mIface, -1, -1);
mNMService.setInterfaceThrottle(mIface,
- mPolicyThrottleValue, mPolicyThrottleValue);
+ mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
} catch (Exception e) {
Slog.e(TAG, "error setting Throttle: " + e);
}
@@ -523,7 +530,8 @@ public class ThrottleService extends IThrottleManager.Stub {
private void checkThrottleAndPostNotification(long currentTotal) {
// is throttling enabled?
- if (mPolicyThreshold == 0) {
+ long threshold = mPolicyThreshold.get();
+ if (threshold == 0) {
clearThrottleAndNotification();
return;
}
@@ -535,15 +543,13 @@ public class ThrottleService extends IThrottleManager.Stub {
}
// check if we need to throttle
- if (currentTotal > mPolicyThreshold) {
- if (mThrottleIndex != 1) {
- synchronized (ThrottleService.this) {
- mThrottleIndex = 1;
- }
- if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!");
+ if (currentTotal > threshold) {
+ if (mThrottleIndex.get() != 1) {
+ mThrottleIndex.set(1);
+ if (DBG) Slog.d(TAG, "Threshold " + threshold + " exceeded!");
try {
mNMService.setInterfaceThrottle(mIface,
- mPolicyThrottleValue, mPolicyThrottleValue);
+ mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
} catch (Exception e) {
Slog.e(TAG, "error setting Throttle: " + e);
}
@@ -556,7 +562,8 @@ public class ThrottleService extends IThrottleManager.Stub {
Notification.FLAG_ONGOING_EVENT);
Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
- broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue);
+ broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
+ mPolicyThrottleValue.get());
mContext.sendStickyBroadcast(broadcast);
} // else already up!
@@ -579,8 +586,8 @@ public class ThrottleService extends IThrottleManager.Stub {
long periodLength = end - start;
long now = System.currentTimeMillis();
long timeUsed = now - start;
- long warningThreshold = 2*mPolicyThreshold*timeUsed/(timeUsed+periodLength);
- if ((currentTotal > warningThreshold) && (currentTotal > mPolicyThreshold/4)) {
+ long warningThreshold = 2*threshold*timeUsed/(timeUsed+periodLength);
+ if ((currentTotal > warningThreshold) && (currentTotal > threshold/4)) {
if (mWarningNotificationSent == false) {
mWarningNotificationSent = true;
mNotificationManager.cancel(R.drawable.stat_sys_throttled);
@@ -625,11 +632,9 @@ public class ThrottleService extends IThrottleManager.Stub {
}
- private synchronized void clearThrottleAndNotification() {
- if (mThrottleIndex != THROTTLE_INDEX_UNTHROTTLED) {
- synchronized (ThrottleService.this) {
- mThrottleIndex = THROTTLE_INDEX_UNTHROTTLED;
- }
+ private void clearThrottleAndNotification() {
+ if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) {
+ mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED);
try {
mNMService.setInterfaceThrottle(mIface, -1, -1);
} catch (Exception e) {
@@ -687,12 +692,12 @@ public class ThrottleService extends IThrottleManager.Stub {
}
private void onResetAlarm() {
- if (VDBG || (mPolicyThreshold != 0)) {
+ if (VDBG || (mPolicyThreshold.get() != 0)) {
Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
" bytes read and " + mRecorder.getPeriodTx(0) + " written");
}
- long now = getBestTime();
+ long now = getBestTime(false);
if (mNtpActive || (mNtpServer == null)) {
Calendar end = calculatePeriodEnd(now);
@@ -719,20 +724,23 @@ public class ThrottleService extends IThrottleManager.Stub {
// will try to get the ntp time and switch to it if found.
// will also cache the time so we don't fetch it repeatedly.
- getBestTime();
+ getBestTime(false);
}
private static final int MAX_NTP_CACHE_AGE_SEC = 60 * 60 * 24; // 1 day
- private static final int MAX_NTP_FETCH_WAIT = 10 * 1000;
+ private static final int MAX_NTP_FETCH_WAIT = 20 * 1000;
private int mMaxNtpCacheAgeSec = MAX_NTP_CACHE_AGE_SEC;
private long cachedNtp;
private long cachedNtpTimestamp;
- private long getBestTime() {
+ // if the request is tied to UI and ANR's are a danger, request a fast result
+ // the regular polling should have updated the cached time recently using the
+ // slower method (!fast)
+ private long getBestTime(boolean fast) {
if (mNtpServer != null) {
if (mNtpActive) {
long ntpAge = SystemClock.elapsedRealtime() - cachedNtpTimestamp;
- if (ntpAge < mMaxNtpCacheAgeSec * 1000) {
+ if (ntpAge < mMaxNtpCacheAgeSec * 1000 || fast) {
if (VDBG) Slog.v(TAG, "using cached time");
return cachedNtp + ntpAge;
}
@@ -1025,39 +1033,57 @@ public class ThrottleService extends IThrottleManager.Stub {
if (DBG) Slog.d(TAG, "data file empty");
return;
}
- synchronized (mParent) {
- String[] parsed = data.split(":");
- int parsedUsed = 0;
- if (parsed.length < 6) {
- Slog.e(TAG, "reading data file with insufficient length - ignoring");
- return;
- }
+ String[] parsed = data.split(":");
+ int parsedUsed = 0;
+ if (parsed.length < 6) {
+ Slog.e(TAG, "reading data file with insufficient length - ignoring");
+ return;
+ }
+ int periodCount;
+ long[] periodRxData;
+ long[] periodTxData;
+ int currentPeriod;
+ Calendar periodStart;
+ Calendar periodEnd;
+ try {
if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
Slog.e(TAG, "reading data file with bad version - ignoring");
return;
}
- mPeriodCount = Integer.parseInt(parsed[parsedUsed++]);
- if (parsed.length != 5 + (2 * mPeriodCount)) {
+ periodCount = Integer.parseInt(parsed[parsedUsed++]);
+ if (parsed.length != 5 + (2 * periodCount)) {
Slog.e(TAG, "reading data file with bad length (" + parsed.length +
- " != " + (5+(2*mPeriodCount)) + ") - ignoring");
+ " != " + (5 + (2 * periodCount)) + ") - ignoring");
return;
}
-
- mPeriodRxData = new long[mPeriodCount];
- for(int i = 0; i < mPeriodCount; i++) {
- mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
+ periodRxData = new long[periodCount];
+ for (int i = 0; i < periodCount; i++) {
+ periodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
}
- mPeriodTxData = new long[mPeriodCount];
- for(int i = 0; i < mPeriodCount; i++) {
- mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
+ periodTxData = new long[periodCount];
+ for (int i = 0; i < periodCount; i++) {
+ periodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
}
- mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]);
- mPeriodStart = new GregorianCalendar();
- mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
- mPeriodEnd = new GregorianCalendar();
- mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
+
+ currentPeriod = Integer.parseInt(parsed[parsedUsed++]);
+
+ periodStart = new GregorianCalendar();
+ periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
+ periodEnd = new GregorianCalendar();
+ periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
+ } catch (Exception e) {
+ Slog.e(TAG, "Error parsing data file - ignoring");
+ return;
+ }
+ synchronized (mParent) {
+ mPeriodCount = periodCount;
+ mPeriodRxData = periodRxData;
+ mPeriodTxData = periodTxData;
+ mCurrentPeriod = currentPeriod;
+ mPeriodStart = periodStart;
+ mPeriodEnd = periodEnd;
}
}
@@ -1091,15 +1117,15 @@ public class ThrottleService extends IThrottleManager.Stub {
}
pw.println();
- pw.println("The threshold is " + mPolicyThreshold +
+ pw.println("The threshold is " + mPolicyThreshold.get() +
", after which you experince throttling to " +
- mPolicyThrottleValue + "kbps");
+ mPolicyThrottleValue.get() + "kbps");
pw.println("Current period is " +
(mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
"and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
" seconds.");
pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
- pw.println("Current Throttle Index is " + mThrottleIndex);
+ pw.println("Current Throttle Index is " + mThrottleIndex.get());
pw.println("Max NTP Cache Age is " + mMaxNtpCacheAgeSec);
for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java
deleted file mode 100644
index d08fe9b..0000000
--- a/services/java/com/android/server/UsbObserver.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * 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;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.hardware.Usb;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UEventObserver;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.util.ArrayList;
-
-/**
- * <p>UsbObserver monitors for changes to USB state.
- */
-class UsbObserver extends UEventObserver {
- private static final String TAG = UsbObserver.class.getSimpleName();
- private static final boolean LOG = false;
-
- private static final String USB_CONFIGURATION_MATCH = "DEVPATH=/devices/virtual/switch/usb_configuration";
- private static final String USB_FUNCTIONS_MATCH = "DEVPATH=/devices/virtual/usb_composite/";
- private static final String USB_CONFIGURATION_PATH = "/sys/class/switch/usb_configuration/state";
- private static final String USB_COMPOSITE_CLASS_PATH = "/sys/class/usb_composite";
-
- private static final int MSG_UPDATE = 0;
-
- private int mUsbConfig = 0;
- private int mPreviousUsbConfig = 0;
-
- // lists of enabled and disabled USB functions
- private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
- private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
-
- private boolean mSystemReady;
-
- private final Context mContext;
-
- private PowerManagerService mPowerManager;
-
- public UsbObserver(Context context) {
- mContext = context;
- init(); // set initial status
-
- startObserving(USB_CONFIGURATION_MATCH);
- startObserving(USB_FUNCTIONS_MATCH);
- }
-
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "USB UEVENT: " + event.toString());
- }
-
- synchronized (this) {
- String switchState = event.get("SWITCH_STATE");
- if (switchState != null) {
- try {
- int newConfig = Integer.parseInt(switchState);
- if (newConfig != mUsbConfig) {
- mPreviousUsbConfig = mUsbConfig;
- mUsbConfig = newConfig;
- // trigger an Intent broadcast
- if (mSystemReady) {
- update();
- }
- }
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
- }
- } else {
- String function = event.get("FUNCTION");
- String enabledStr = event.get("ENABLED");
- if (function != null && enabledStr != null) {
- // Note: we do not broadcast a change when a function is enabled or disabled.
- // We just record the state change for the next broadcast.
- boolean enabled = "1".equals(enabledStr);
- if (enabled) {
- if (!mEnabledFunctions.contains(function)) {
- mEnabledFunctions.add(function);
- }
- mDisabledFunctions.remove(function);
- } else {
- if (!mDisabledFunctions.contains(function)) {
- mDisabledFunctions.add(function);
- }
- mEnabledFunctions.remove(function);
- }
- }
- }
- }
- }
- private final void init() {
- char[] buffer = new char[1024];
-
- try {
- FileReader file = new FileReader(USB_CONFIGURATION_PATH);
- int len = file.read(buffer, 0, 1024);
- mPreviousUsbConfig = mUsbConfig = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have USB configuration switch support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
-
- try {
- File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
- for (int i = 0; i < files.length; i++) {
- File file = new File(files[i], "enable");
- FileReader reader = new FileReader(file);
- int len = reader.read(buffer, 0, 1024);
- int value = Integer.valueOf((new String(buffer, 0, len)).trim());
- String functionName = files[i].getName();
- if (value == 1) {
- mEnabledFunctions.add(functionName);
- } else {
- mDisabledFunctions.add(functionName);
- }
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have USB composite class support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
- }
-
- void systemReady() {
- synchronized (this) {
- update();
- mSystemReady = true;
- }
- }
-
- private final void update() {
- mHandler.sendEmptyMessage(MSG_UPDATE);
- }
-
- private final Handler mHandler = new Handler() {
- private void addEnabledFunctions(Intent intent) {
- // include state of all USB functions in our extras
- for (int i = 0; i < mEnabledFunctions.size(); i++) {
- intent.putExtra(mEnabledFunctions.get(i), Usb.USB_FUNCTION_ENABLED);
- }
- for (int i = 0; i < mDisabledFunctions.size(); i++) {
- intent.putExtra(mDisabledFunctions.get(i), Usb.USB_FUNCTION_DISABLED);
- }
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE:
- synchronized (this) {
- final ContentResolver cr = mContext.getContentResolver();
-
- if (Settings.Secure.getInt(cr,
- Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
- Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
- return;
- }
- // Send an Intent containing connected/disconnected state
- // and the enabled/disabled state of all USB functions
- Intent intent;
- boolean usbConnected = (mUsbConfig != 0);
- if (usbConnected) {
- intent = new Intent(Usb.ACTION_USB_CONNECTED);
- addEnabledFunctions(intent);
- } else {
- intent = new Intent(Usb.ACTION_USB_DISCONNECTED);
- }
- mContext.sendBroadcast(intent);
-
- // send a sticky broadcast for clients interested in both connect and disconnect
- intent = new Intent(Usb.ACTION_USB_STATE);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(Usb.USB_CONNECTED, usbConnected);
- addEnabledFunctions(intent);
- mContext.sendStickyBroadcast(intent);
- }
- break;
- }
- }
- };
-}
diff --git a/services/java/com/android/server/UsbService.java b/services/java/com/android/server/UsbService.java
new file mode 100644
index 0000000..8ef03d4
--- /dev/null
+++ b/services/java/com/android/server/UsbService.java
@@ -0,0 +1,252 @@
+/*
+ * 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;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.UsbManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UEventObserver;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.util.ArrayList;
+
+/**
+ * <p>UsbService monitors for changes to USB state.
+ */
+class UsbService {
+ private static final String TAG = UsbService.class.getSimpleName();
+ private static final boolean LOG = false;
+
+ private static final String USB_CONNECTED_MATCH =
+ "DEVPATH=/devices/virtual/switch/usb_connected";
+ private static final String USB_CONFIGURATION_MATCH =
+ "DEVPATH=/devices/virtual/switch/usb_configuration";
+ private static final String USB_FUNCTIONS_MATCH =
+ "DEVPATH=/devices/virtual/usb_composite/";
+ private static final String USB_CONNECTED_PATH =
+ "/sys/class/switch/usb_connected/state";
+ private static final String USB_CONFIGURATION_PATH =
+ "/sys/class/switch/usb_configuration/state";
+ private static final String USB_COMPOSITE_CLASS_PATH =
+ "/sys/class/usb_composite";
+
+ private static final int MSG_UPDATE = 0;
+
+ // Delay for debouncing USB disconnects.
+ // We often get rapid connect/disconnect events when enabling USB functions,
+ // which need debouncing.
+ private static final int UPDATE_DELAY = 1000;
+
+ // current connected and configuration state
+ private int mConnected;
+ private int mConfiguration;
+
+ // last broadcasted connected and configuration state
+ private int mLastConnected = -1;
+ private int mLastConfiguration = -1;
+
+ // lists of enabled and disabled USB functions
+ private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
+ private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
+
+ private boolean mSystemReady;
+
+ private final Context mContext;
+
+ private final UEventObserver mUEventObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "USB UEVENT: " + event.toString());
+ }
+
+ synchronized (this) {
+ String name = event.get("SWITCH_NAME");
+ String state = event.get("SWITCH_STATE");
+ if (name != null && state != null) {
+ try {
+ int intState = Integer.parseInt(state);
+ if ("usb_connected".equals(name)) {
+ mConnected = intState;
+ // trigger an Intent broadcast
+ if (mSystemReady) {
+ // debounce disconnects
+ update(mConnected == 0);
+ }
+ } else if ("usb_configuration".equals(name)) {
+ mConfiguration = intState;
+ // trigger an Intent broadcast
+ if (mSystemReady) {
+ update(mConnected == 0);
+ }
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Could not parse switch state from event " + event);
+ }
+ } else {
+ String function = event.get("FUNCTION");
+ String enabledStr = event.get("ENABLED");
+ if (function != null && enabledStr != null) {
+ // Note: we do not broadcast a change when a function is enabled or disabled.
+ // We just record the state change for the next broadcast.
+ boolean enabled = "1".equals(enabledStr);
+ if (enabled) {
+ if (!mEnabledFunctions.contains(function)) {
+ mEnabledFunctions.add(function);
+ }
+ mDisabledFunctions.remove(function);
+ } else {
+ if (!mDisabledFunctions.contains(function)) {
+ mDisabledFunctions.add(function);
+ }
+ mEnabledFunctions.remove(function);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ public UsbService(Context context) {
+ mContext = context;
+ init(); // set initial status
+
+ if (mConfiguration >= 0) {
+ mUEventObserver.startObserving(USB_CONNECTED_MATCH);
+ mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
+ mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
+ }
+ }
+
+ private final void init() {
+ char[] buffer = new char[1024];
+
+ mConfiguration = -1;
+ try {
+ FileReader file = new FileReader(USB_CONNECTED_PATH);
+ int len = file.read(buffer, 0, 1024);
+ file.close();
+ mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ file = new FileReader(USB_CONFIGURATION_PATH);
+ len = file.read(buffer, 0, 1024);
+ file.close();
+ mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ } catch (FileNotFoundException e) {
+ Slog.i(TAG, "This kernel does not have USB configuration switch support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+ if (mConfiguration < 0)
+ return;
+
+ try {
+ File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
+ for (int i = 0; i < files.length; i++) {
+ File file = new File(files[i], "enable");
+ FileReader reader = new FileReader(file);
+ int len = reader.read(buffer, 0, 1024);
+ reader.close();
+ int value = Integer.valueOf((new String(buffer, 0, len)).trim());
+ String functionName = files[i].getName();
+ if (value == 1) {
+ mEnabledFunctions.add(functionName);
+ } else {
+ mDisabledFunctions.add(functionName);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "This kernel does not have USB composite class support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+ }
+
+ private void initHostSupport() {
+ // temporarily disabled
+ }
+
+ void systemReady() {
+ synchronized (this) {
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_hasUsbHostSupport)) {
+ // start monitoring for connected USB devices
+ initHostSupport();
+ }
+
+ update(false);
+ mSystemReady = true;
+ }
+ }
+
+ private final void update(boolean delayed) {
+ mHandler.removeMessages(MSG_UPDATE);
+ mHandler.sendEmptyMessageDelayed(MSG_UPDATE, delayed ? UPDATE_DELAY : 0);
+ }
+
+ private final Handler mHandler = new Handler() {
+ private void addEnabledFunctions(Intent intent) {
+ // include state of all USB functions in our extras
+ for (int i = 0; i < mEnabledFunctions.size(); i++) {
+ intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
+ }
+ for (int i = 0; i < mDisabledFunctions.size(); i++) {
+ intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE:
+ synchronized (this) {
+ if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
+
+ final ContentResolver cr = mContext.getContentResolver();
+ if (Settings.Secure.getInt(cr,
+ Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
+ Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
+ return;
+ }
+
+ mLastConnected = mConnected;
+ mLastConfiguration = mConfiguration;
+
+ // send a sticky broadcast containing current USB state
+ Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
+ intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
+ addEnabledFunctions(intent);
+ mContext.sendStickyBroadcast(intent);
+ }
+ }
+ break;
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index f0b5955..2fcdb5d 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -112,6 +112,10 @@ public class VibratorService extends IVibratorService.Stub {
context.registerReceiver(mIntentReceiver, filter);
}
+ public boolean hasVibrator() {
+ return vibratorExists();
+ }
+
public void vibrate(long milliseconds, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -380,6 +384,7 @@ public class VibratorService extends IVibratorService.Stub {
volatile VibrateThread mThread;
+ native static boolean vibratorExists();
native static void vibratorOn(long milliseconds);
native static void vibratorOff();
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 859c85c..997e750 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -587,6 +587,8 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
mIWindowManager.removeWindowToken(mWallpaperConnection.mToken);
} catch (RemoteException e) {
}
+ mWallpaperConnection.mService = null;
+ mWallpaperConnection.mEngine = null;
mWallpaperConnection = null;
}
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5aa0111..cf07239 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -16,92 +16,76 @@
package com.android.server;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
-
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
-
import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiNative;
-import android.net.wifi.WifiStateTracker;
+import android.net.wifi.WifiStateMachine;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WpsConfiguration;
+import android.net.wifi.WpsResult;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
-import android.net.NetworkStateTracker;
import android.net.DhcpInfo;
-import android.net.NetworkUtils;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkManagementService;
-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.WorkSource;
import android.provider.Settings;
-import android.util.Slog;
import android.text.TextUtils;
+import android.util.Slog;
+import java.net.InetAddress;
import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.regex.Pattern;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.net.UnknownHostException;
import com.android.internal.app.IBatteryStats;
-import android.app.backup.IBackupManager;
+import com.android.internal.util.AsyncChannel;
import com.android.server.am.BatteryStatsService;
import com.android.internal.R;
/**
* WifiService handles remote WiFi operation requests by implementing
- * the IWifiManager interface. It also creates a WifiMonitor to listen
- * for Wifi-related events.
+ * the IWifiManager interface.
*
* @hide
*/
+//TODO: Clean up multiple locks and implement WifiService
+// as a SM to track soft AP/client/adhoc bring up based
+// on device idle state, airplane mode and boot.
+
public class WifiService extends IWifiManager.Stub {
private static final String TAG = "WifiService";
- private static final boolean DBG = false;
- private static final Pattern scanResultPattern = Pattern.compile("\t+");
- private final WifiStateTracker mWifiStateTracker;
- /* TODO: fetch a configurable interface */
- private static final String SOFTAP_IFACE = "wl0.1";
+ private static final boolean DBG = true;
+
+ private final WifiStateMachine mWifiStateMachine;
private Context mContext;
- private int mWifiApState;
private AlarmManager mAlarmManager;
private PendingIntent mIdleIntent;
@@ -110,10 +94,8 @@ public class WifiService extends IWifiManager.Stub {
private boolean mDeviceIdle;
private int mPluggedType;
- private enum DriverAction {DRIVER_UNLOAD, NO_DRIVER_UNLOAD};
-
// true if the user enabled Wifi while in airplane mode
- private boolean mAirplaneModeOverwridden;
+ private AtomicBoolean mAirplaneModeOverwridden = new AtomicBoolean(false);
private final LockList mLocks = new LockList();
// some wifi lock statistics
@@ -131,9 +113,7 @@ public class WifiService extends IWifiManager.Stub {
private final IBatteryStats mBatteryStats;
- private INetworkManagementService nwService;
ConnectivityManager mCm;
- private WifiWatchdogService mWifiWatchdogService = null;
private String[] mWifiRegexs;
/**
@@ -143,122 +123,130 @@ public class WifiService extends IWifiManager.Stub {
* being enabled but not active exceeds the battery drain caused by
* re-establishing a connection to the mobile data network.
*/
- private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */
+ private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
+
+ private static final String ACTION_DEVICE_IDLE =
+ "com.android.server.WifiManager.action.DEVICE_IDLE";
+
+ private boolean mIsReceiverRegistered = false;
+
- private static final String WAKELOCK_TAG = "*wifi*";
+ NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
+ // Variables relating to the 'available networks' notification
/**
- * The maximum amount of time to hold the wake lock after a disconnect
- * caused by stopping the driver. Establishing an EDGE connection has been
- * observed to take about 5 seconds under normal circumstances. This
- * provides a bit of extra margin.
- * <p>
- * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}.
- * This is the default value if a Settings.Secure value is not present.
+ * The icon to show in the 'available networks' notification. This will also
+ * be the ID of the Notification given to the NotificationManager.
*/
- private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000;
-
- // Wake lock used by driver-stop operation
- private static PowerManager.WakeLock sDriverStopWakeLock;
- // Wake lock used by other operations
- private static PowerManager.WakeLock sWakeLock;
-
- private static final int MESSAGE_ENABLE_WIFI = 0;
- private static final int MESSAGE_DISABLE_WIFI = 1;
- private static final int MESSAGE_STOP_WIFI = 2;
- private static final int MESSAGE_START_WIFI = 3;
- private static final int MESSAGE_RELEASE_WAKELOCK = 4;
- private static final int MESSAGE_UPDATE_STATE = 5;
- private static final int MESSAGE_START_ACCESS_POINT = 6;
- private static final int MESSAGE_STOP_ACCESS_POINT = 7;
- private static final int MESSAGE_SET_CHANNELS = 8;
- private static final int MESSAGE_ENABLE_NETWORKS = 9;
- private static final int MESSAGE_START_SCAN = 10;
- private static final int MESSAGE_REPORT_WORKSOURCE = 11;
- private static final int MESSAGE_ENABLE_RSSI_POLLING = 12;
-
-
- private final WifiHandler mWifiHandler;
-
- /*
- * Cache of scan results objects (size is somewhat arbitrary)
+ private static final int ICON_NETWORKS_AVAILABLE =
+ com.android.internal.R.drawable.stat_notify_wifi_in_range;
+ /**
+ * When a notification is shown, we wait this amount before possibly showing it again.
*/
- private static final int SCAN_RESULT_CACHE_SIZE = 80;
- private final LinkedHashMap<String, ScanResult> mScanResultCache;
-
- /*
- * Character buffer used to parse scan results (optimization)
+ private final long NOTIFICATION_REPEAT_DELAY_MS;
+ /**
+ * Whether the user has set the setting to show the 'available networks' notification.
*/
- private static final int SCAN_RESULT_BUFFER_SIZE = 512;
- private boolean mNeedReconfig;
-
+ private boolean mNotificationEnabled;
/**
- * Temporary for computing UIDS that are responsible for starting WIFI.
- * Protected by mWifiStateTracker lock.
+ * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
*/
- private final WorkSource mTmpWorkSource = new WorkSource();
-
- /*
- * Last UID that asked to enable WIFI.
+ private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
+ /**
+ * The {@link System#currentTimeMillis()} must be at least this value for us
+ * to show the notification again.
*/
- private int mLastEnableUid = Process.myUid();
-
- /*
- * Last UID that asked to enable WIFI AP.
+ private long mNotificationRepeatTime;
+ /**
+ * The Notification object given to the NotificationManager.
*/
- private int mLastApEnableUid = Process.myUid();
-
-
+ private Notification mNotification;
+ /**
+ * Whether the notification is being shown, as set by us. That is, if the
+ * user cancels the notification, we will not receive the callback so this
+ * will still be true. We only guarantee if this is false, then the
+ * notification is not showing.
+ */
+ private boolean mNotificationShown;
+ /**
+ * The number of continuous scans that must occur before consider the
+ * supplicant in a scanning state. This allows supplicant to associate with
+ * remembered networks that are in the scan results.
+ */
+ private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
/**
- * Number of allowed radio frequency channels in various regulatory domains.
- * This list is sufficient for 802.11b/g networks (2.4GHz range).
+ * The number of scans since the last network state change. When this
+ * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
+ * supplicant to actually be scanning. When the network state changes to
+ * something other than scanning, we reset this to 0.
*/
- private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14};
+ private int mNumScansSinceNetworkStateChange;
- private static final String ACTION_DEVICE_IDLE =
- "com.android.server.WifiManager.action.DEVICE_IDLE";
+ /**
+ * Asynchronous channel to WifiStateMachine
+ */
+ private AsyncChannel mChannel;
- WifiService(Context context, WifiStateTracker tracker) {
- mContext = context;
- mWifiStateTracker = tracker;
- mWifiStateTracker.enableRssiPolling(true);
- mBatteryStats = BatteryStatsService.getService();
+ /**
+ * TODO: Possibly change WifiService into an AsyncService.
+ */
+ private class WifiServiceHandler extends Handler {
+ private AsyncChannel mWshChannel;
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- nwService = INetworkManagementService.Stub.asInterface(b);
+ WifiServiceHandler(android.os.Looper looper, Context context) {
+ super(looper);
+ mWshChannel = new AsyncChannel();
+ mWshChannel.connect(context, this, mWifiStateMachine.getHandler());
+ }
- mScanResultCache = new LinkedHashMap<String, ScanResult>(
- SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
- /*
- * Limit the cache size by SCAN_RESULT_CACHE_SIZE
- * elements
- */
- public boolean removeEldestEntry(Map.Entry eldest) {
- return SCAN_RESULT_CACHE_SIZE < this.size();
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ mChannel = mWshChannel;
+ } else {
+ Slog.d(TAG, "WifiServicehandler.handleMessage could not connect error=" +
+ msg.arg1);
+ mChannel = null;
+ }
+ break;
}
- };
+ default: {
+ Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
+ break;
+ }
+ }
+ }
+ }
+ WifiServiceHandler mHandler;
- HandlerThread wifiThread = new HandlerThread("WifiService");
- wifiThread.start();
- mWifiHandler = new WifiHandler(wifiThread.getLooper());
+ /**
+ * Temporary for computing UIDS that are responsible for starting WIFI.
+ * Protected by mWifiStateTracker lock.
+ */
+ private final WorkSource mTmpWorkSource = new WorkSource();
- mWifiStateTracker.setWifiState(WIFI_STATE_DISABLED);
- mWifiApState = WIFI_AP_STATE_DISABLED;
+ WifiService(Context context) {
+ mContext = context;
+ mWifiStateMachine = new WifiStateMachine(mContext);
+ mWifiStateMachine.enableRssiPolling(true);
+ mBatteryStats = BatteryStatsService.getService();
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
- PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
- sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+ HandlerThread wifiThread = new HandlerThread("WifiService");
+ wifiThread.start();
+ mHandler = new WifiServiceHandler(wifiThread.getLooper(), context);
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// clear our flag indicating the user has overwridden airplane mode
- mAirplaneModeOverwridden = false;
+ mAirplaneModeOverwridden.set(false);
// on airplane disable, restore Wifi if the saved state indicates so
if (!isAirplaneModeOn() && testAndClearWifiSavedState()) {
persistWifiEnabled(true);
@@ -273,14 +261,50 @@ public class WifiService extends IWifiManager.Stub {
@Override
public void onReceive(Context context, Intent intent) {
- ArrayList<String> available = intent.getStringArrayListExtra(
- ConnectivityManager.EXTRA_AVAILABLE_TETHER);
- ArrayList<String> active = intent.getStringArrayListExtra(
- ConnectivityManager.EXTRA_ACTIVE_TETHER);
- updateTetherState(available, active);
+ ArrayList<String> available = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+ ArrayList<String> active = intent.getStringArrayListExtra(
+ ConnectivityManager.EXTRA_ACTIVE_TETHER);
+ updateTetherState(available, active);
}
},new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ // reset & clear notification on any wifi state change
+ resetNotification();
+ } else if (intent.getAction().equals(
+ WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ // reset & clear notification on a network connect & disconnect
+ switch(mNetworkInfo.getDetailedState()) {
+ case CONNECTED:
+ case DISCONNECTED:
+ resetNotification();
+ break;
+ }
+ } else if (intent.getAction().equals(
+ WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ checkAndSetNotification();
+ }
+ }
+ }, filter);
+
+ // Setting is in seconds
+ NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
+ mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
+ mNotificationEnabledSettingObserver.register();
}
/**
@@ -289,7 +313,7 @@ public class WifiService extends IWifiManager.Stub {
*
* This function is used only at boot time
*/
- public void startWifi() {
+ public void checkAndStartWifi() {
/* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */
boolean wifiEnabled = !isAirplaneModeOn()
&& (getPersistedWifiEnabled() || testAndClearWifiSavedState());
@@ -306,7 +330,10 @@ public class WifiService extends IWifiManager.Stub {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
- mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (mCm == null) {
+ mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
mWifiRegexs = mCm.getTetherableWifiRegexs();
for (String intf : available) {
@@ -318,25 +345,22 @@ public class WifiService extends IWifiManager.Stub {
ifcg = service.getInterfaceConfig(intf);
if (ifcg != null) {
/* IP/netmask: 192.168.43.1/255.255.255.0 */
- ifcg.ipAddr = (192 << 24) + (168 << 16) + (43 << 8) + 1;
- ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
- ifcg.interfaceFlags = "up";
+ ifcg.addr = InetAddress.getByName("192.168.43.1");
+ ifcg.mask = InetAddress.getByName("255.255.255.0");
+ ifcg.interfaceFlags = "[up]";
service.setInterfaceConfig(intf, ifcg);
}
} catch (Exception e) {
Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
- try {
- nwService.stopAccessPoint();
- } catch (Exception ee) {
- Slog.e(TAG, "Could not stop AP, :" + ee);
- }
- setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD);
+ setWifiApEnabled(null, false);
return;
}
if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
- Slog.e(TAG, "Error tethering "+intf);
+ Slog.e(TAG, "Error tethering on " + intf);
+ setWifiApEnabled(null, false);
+ return;
}
break;
}
@@ -372,18 +396,18 @@ public class WifiService extends IWifiManager.Stub {
Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
}
- NetworkStateTracker getNetworkStateTracker() {
- return mWifiStateTracker;
- }
-
/**
* see {@link android.net.wifi.WifiManager#pingSupplicant()}
- * @return {@code true} if the operation succeeds
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean pingSupplicant() {
- enforceChangePermission();
-
- return mWifiStateTracker.ping();
+ enforceAccessPermission();
+ if (mChannel != null) {
+ return mWifiStateMachine.syncPingSupplicant(mChannel);
+ } else {
+ Slog.e(TAG, "mChannel is not initialized");
+ return false;
+ }
}
/**
@@ -391,9 +415,24 @@ public class WifiService extends IWifiManager.Stub {
*/
public void startScan(boolean forceActive) {
enforceChangePermission();
- if (mWifiHandler == null) return;
+ mWifiStateMachine.startScan(forceActive);
+ }
+
+ private void enforceAccessPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
+ "WifiService");
+ }
+
+ private void enforceChangePermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
+ "WifiService");
- Message.obtain(mWifiHandler, MESSAGE_START_SCAN, forceActive ? 1 : 0, 0).sendToTarget();
+ }
+
+ private void enforceMulticastChangePermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
+ "WifiService");
}
/**
@@ -402,168 +441,44 @@ public class WifiService extends IWifiManager.Stub {
* @return {@code true} if the enable/disable operation was
* started or is already in the queue.
*/
- public boolean setWifiEnabled(boolean enable) {
+ public synchronized boolean setWifiEnabled(boolean enable) {
enforceChangePermission();
- if (mWifiHandler == null) return false;
-
- synchronized (mWifiHandler) {
- // caller may not have WAKE_LOCK permission - it's not required here
- long ident = Binder.clearCallingIdentity();
- sWakeLock.acquire();
- Binder.restoreCallingIdentity(ident);
- mLastEnableUid = Binder.getCallingUid();
- // set a flag if the user is enabling Wifi while in airplane mode
- mAirplaneModeOverwridden = (enable && isAirplaneModeOn() && isAirplaneToggleable());
- sendEnableMessage(enable, true, Binder.getCallingUid());
+ if (DBG) {
+ Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
}
- return true;
- }
-
- /**
- * Enables/disables Wi-Fi synchronously.
- * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
- * @param persist {@code true} if the setting should be persisted.
- * @param uid The UID of the process making the request.
- * @return {@code true} if the operation succeeds (or if the existing state
- * is the same as the requested state)
- */
- private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) {
- final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
- final int wifiState = mWifiStateTracker.getWifiState();
-
- if (wifiState == eventualWifiState) {
- return true;
- }
- if (enable && isAirplaneModeOn() && !mAirplaneModeOverwridden) {
- return false;
+ // set a flag if the user is enabling Wifi while in airplane mode
+ if (enable && isAirplaneModeOn() && isAirplaneToggleable()) {
+ mAirplaneModeOverwridden.set(true);
}
- /**
- * Multiple calls to unregisterReceiver() cause exception and a system crash.
- * This can happen if a supplicant is lost (or firmware crash occurs) and user indicates
- * disable wifi at the same time.
- * Avoid doing a disable when the current Wifi state is UNKNOWN
- * TODO: Handle driver load fail and supplicant lost as seperate states
- */
- if ((wifiState == WIFI_STATE_UNKNOWN) && !enable) {
- return false;
+ if (enable) {
+ reportStartWorkSource();
}
+ mWifiStateMachine.setWifiEnabled(enable);
- /**
- * Fail Wifi if AP is enabled
- * TODO: Deprecate WIFI_STATE_UNKNOWN and rename it
- * WIFI_STATE_FAILED
+ /*
+ * Caller might not have WRITE_SECURE_SETTINGS,
+ * only CHANGE_WIFI_STATE is enforced
*/
- if ((mWifiApState == WIFI_AP_STATE_ENABLED) && enable) {
- setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
- return false;
- }
-
- setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
+ long ident = Binder.clearCallingIdentity();
+ persistWifiEnabled(enable);
+ Binder.restoreCallingIdentity(ident);
if (enable) {
- if (!mWifiStateTracker.loadDriver()) {
- Slog.e(TAG, "Failed to load Wi-Fi driver.");
- setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
- return false;
- }
- if (!mWifiStateTracker.startSupplicant()) {
- mWifiStateTracker.unloadDriver();
- Slog.e(TAG, "Failed to start supplicant daemon.");
- setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
- return false;
+ if (!mIsReceiverRegistered) {
+ registerForBroadcasts();
+ mIsReceiverRegistered = true;
}
-
- registerForBroadcasts();
- mWifiStateTracker.startEventLoop();
-
- } else {
-
+ } else if (mIsReceiverRegistered){
mContext.unregisterReceiver(mReceiver);
- // Remove notification (it will no-op if it isn't visible)
- mWifiStateTracker.setNotificationVisible(false, 0, false, 0);
-
- boolean failedToStopSupplicantOrUnloadDriver = false;
-
- if (!mWifiStateTracker.stopSupplicant()) {
- Slog.e(TAG, "Failed to stop supplicant daemon.");
- setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
- failedToStopSupplicantOrUnloadDriver = true;
- }
-
- /**
- * Reset connections and disable interface
- * before we unload the driver
- */
- mWifiStateTracker.resetConnections(true);
-
- if (!mWifiStateTracker.unloadDriver()) {
- Slog.e(TAG, "Failed to unload Wi-Fi driver.");
- if (!failedToStopSupplicantOrUnloadDriver) {
- setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
- failedToStopSupplicantOrUnloadDriver = true;
- }
- }
-
- if (failedToStopSupplicantOrUnloadDriver) {
- return false;
- }
+ mIsReceiverRegistered = false;
}
- // Success!
-
- if (persist) {
- persistWifiEnabled(enable);
- }
- setWifiEnabledState(eventualWifiState, uid);
return true;
}
- private void setWifiEnabledState(int wifiState, int uid) {
- final int previousWifiState = mWifiStateTracker.getWifiState();
-
- long ident = Binder.clearCallingIdentity();
- try {
- if (wifiState == WIFI_STATE_ENABLED) {
- mBatteryStats.noteWifiOn();
- } else if (wifiState == WIFI_STATE_DISABLED) {
- mBatteryStats.noteWifiOff();
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- // Update state
- mWifiStateTracker.setWifiState(wifiState);
-
- // Broadcast
- final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
- intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
- mContext.sendStickyBroadcast(intent);
- }
-
- private void enforceAccessPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
- "WifiService");
- }
-
- private void enforceChangePermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
- "WifiService");
-
- }
-
- private void enforceMulticastChangePermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
- "WifiService");
- }
-
/**
* see {@link WifiManager#getWifiState()}
* @return One of {@link WifiManager#WIFI_STATE_DISABLED},
@@ -574,66 +489,59 @@ public class WifiService extends IWifiManager.Stub {
*/
public int getWifiEnabledState() {
enforceAccessPermission();
- return mWifiStateTracker.getWifiState();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#disconnect()}
- * @return {@code true} if the operation succeeds
- */
- public boolean disconnect() {
- enforceChangePermission();
-
- return mWifiStateTracker.disconnect();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#reconnect()}
- * @return {@code true} if the operation succeeds
- */
- public boolean reconnect() {
- enforceChangePermission();
-
- return mWifiStateTracker.reconnectCommand();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#reassociate()}
- * @return {@code true} if the operation succeeds
- */
- public boolean reassociate() {
- enforceChangePermission();
-
- return mWifiStateTracker.reassociate();
+ return mWifiStateMachine.syncGetWifiState();
}
/**
* see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
* @param wifiConfig SSID, security and channel details as
* part of WifiConfiguration
- * @param enabled, true to enable and false to disable
+ * @param enabled true to enable and false to disable
* @return {@code true} if the start operation was
* started or is already in the queue.
*/
- public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
+ public synchronized boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
enforceChangePermission();
- if (mWifiHandler == null) return false;
-
- synchronized (mWifiHandler) {
+ if (enabled) {
+ /* Use default config if there is no existing config */
+ if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) {
+ wifiConfig = new WifiConfiguration();
+ wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
+ wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
+ }
+ /*
+ * Caller might not have WRITE_SECURE_SETTINGS,
+ * only CHANGE_WIFI_STATE is enforced
+ */
long ident = Binder.clearCallingIdentity();
- sWakeLock.acquire();
+ setWifiApConfiguration(wifiConfig);
Binder.restoreCallingIdentity(ident);
-
- mLastApEnableUid = Binder.getCallingUid();
- sendAccessPointMessage(enabled, wifiConfig, Binder.getCallingUid());
}
+ mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
+
return true;
}
- public WifiConfiguration getWifiApConfiguration() {
+ /**
+ * see {@link WifiManager#getWifiApState()}
+ * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
+ * {@link WifiManager#WIFI_AP_STATE_DISABLING},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLED},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLING},
+ * {@link WifiManager#WIFI_AP_STATE_FAILED}
+ */
+ public int getWifiApEnabledState() {
enforceAccessPermission();
+ return mWifiStateMachine.syncGetWifiApState();
+ }
+
+ /**
+ * see {@link WifiManager#getWifiApConfiguration()}
+ * @return soft access point configuration
+ */
+ public synchronized WifiConfiguration getWifiApConfiguration() {
final ContentResolver cr = mContext.getContentResolver();
WifiConfiguration wifiConfig = new WifiConfiguration();
int authType;
@@ -651,7 +559,11 @@ public class WifiService extends IWifiManager.Stub {
}
}
- public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
+ /**
+ * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
+ * @param wifiConfig WifiConfiguration details for soft access point
+ */
+ public synchronized void setWifiApConfiguration(WifiConfiguration wifiConfig) {
enforceChangePermission();
final ContentResolver cr = mContext.getContentResolver();
boolean isWpa;
@@ -667,143 +579,27 @@ public class WifiService extends IWifiManager.Stub {
}
/**
- * Enables/disables Wi-Fi AP synchronously. The driver is loaded
- * and soft access point configured as a single operation.
- * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
- * @param uid The UID of the process making the request.
- * @param wifiConfig The WifiConfiguration for AP
- * @return {@code true} if the operation succeeds (or if the existing state
- * is the same as the requested state)
+ * see {@link android.net.wifi.WifiManager#disconnect()}
*/
- private boolean setWifiApEnabledBlocking(boolean enable,
- int uid, WifiConfiguration wifiConfig) {
- final int eventualWifiApState = enable ? WIFI_AP_STATE_ENABLED : WIFI_AP_STATE_DISABLED;
-
- if (mWifiApState == eventualWifiApState) {
- /* Configuration changed on a running access point */
- if(enable && (wifiConfig != null)) {
- try {
- nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
- SOFTAP_IFACE);
- setWifiApConfiguration(wifiConfig);
- return true;
- } catch(Exception e) {
- Slog.e(TAG, "Exception in nwService during AP restart");
- try {
- nwService.stopAccessPoint();
- } catch (Exception ee) {
- Slog.e(TAG, "Could not stop AP, :" + ee);
- }
- setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
- return false;
- }
- } else {
- return true;
- }
- }
-
- /**
- * Fail AP if Wifi is enabled
- */
- if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLED) && enable) {
- setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
- return false;
- }
-
- setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING :
- WIFI_AP_STATE_DISABLING, uid, DriverAction.NO_DRIVER_UNLOAD);
-
- if (enable) {
-
- /* Use default config if there is no existing config */
- if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) {
- wifiConfig = new WifiConfiguration();
- wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
- wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
- }
-
- if (!mWifiStateTracker.loadDriver()) {
- Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode");
- setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
- return false;
- }
-
- try {
- nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
- SOFTAP_IFACE);
- } catch(Exception e) {
- Slog.e(TAG, "Exception in startAccessPoint()");
- setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
- return false;
- }
-
- setWifiApConfiguration(wifiConfig);
-
- } else {
-
- try {
- nwService.stopAccessPoint();
- } catch(Exception e) {
- Slog.e(TAG, "Exception in stopAccessPoint()");
- setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
- return false;
- }
-
- if (!mWifiStateTracker.unloadDriver()) {
- Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode");
- setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
- return false;
- }
- }
-
- setWifiApEnabledState(eventualWifiApState, uid, DriverAction.NO_DRIVER_UNLOAD);
- return true;
+ public void disconnect() {
+ enforceChangePermission();
+ mWifiStateMachine.disconnectCommand();
}
/**
- * see {@link WifiManager#getWifiApState()}
- * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
- * {@link WifiManager#WIFI_AP_STATE_DISABLING},
- * {@link WifiManager#WIFI_AP_STATE_ENABLED},
- * {@link WifiManager#WIFI_AP_STATE_ENABLING},
- * {@link WifiManager#WIFI_AP_STATE_FAILED}
+ * see {@link android.net.wifi.WifiManager#reconnect()}
*/
- public int getWifiApEnabledState() {
- enforceAccessPermission();
- return mWifiApState;
+ public void reconnect() {
+ enforceChangePermission();
+ mWifiStateMachine.reconnectCommand();
}
- private void setWifiApEnabledState(int wifiAPState, int uid, DriverAction flag) {
- final int previousWifiApState = mWifiApState;
-
- /**
- * Unload the driver if going to a failed state
- */
- if ((mWifiApState == WIFI_AP_STATE_FAILED) && (flag == DriverAction.DRIVER_UNLOAD)) {
- mWifiStateTracker.unloadDriver();
- }
-
- long ident = Binder.clearCallingIdentity();
- try {
- if (wifiAPState == WIFI_AP_STATE_ENABLED) {
- mBatteryStats.noteWifiOn();
- } else if (wifiAPState == WIFI_AP_STATE_DISABLED) {
- mBatteryStats.noteWifiOff();
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- // Update state
- mWifiApState = wifiAPState;
-
- // Broadcast
- final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiAPState);
- intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
- mContext.sendStickyBroadcast(intent);
+ /**
+ * see {@link android.net.wifi.WifiManager#reassociate()}
+ */
+ public void reassociate() {
+ enforceChangePermission();
+ mWifiStateMachine.reassociateCommand();
}
/**
@@ -812,217 +608,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public List<WifiConfiguration> getConfiguredNetworks() {
enforceAccessPermission();
- String listStr;
-
- /*
- * We don't cache the list, because we want to allow
- * for the possibility that the configuration file
- * has been modified through some external means,
- * such as the wpa_cli command line program.
- */
- listStr = mWifiStateTracker.listNetworks();
-
- List<WifiConfiguration> networks =
- new ArrayList<WifiConfiguration>();
- if (listStr == null)
- return networks;
-
- String[] lines = listStr.split("\n");
- // Skip the first line, which is a header
- for (int i = 1; i < lines.length; i++) {
- String[] result = lines[i].split("\t");
- // network-id | ssid | bssid | flags
- WifiConfiguration config = new WifiConfiguration();
- try {
- config.networkId = Integer.parseInt(result[0]);
- } catch(NumberFormatException e) {
- continue;
- }
- if (result.length > 3) {
- if (result[3].indexOf("[CURRENT]") != -1)
- config.status = WifiConfiguration.Status.CURRENT;
- else if (result[3].indexOf("[DISABLED]") != -1)
- config.status = WifiConfiguration.Status.DISABLED;
- else
- config.status = WifiConfiguration.Status.ENABLED;
- } else {
- config.status = WifiConfiguration.Status.ENABLED;
- }
- readNetworkVariables(config);
- networks.add(config);
- }
-
- return networks;
- }
-
- /**
- * Read the variables from the supplicant daemon that are needed to
- * fill in the WifiConfiguration object.
- * <p/>
- * The caller must hold the synchronization monitor.
- * @param config the {@link WifiConfiguration} object to be filled in.
- */
- private void readNetworkVariables(WifiConfiguration config) {
-
- int netId = config.networkId;
- if (netId < 0)
- return;
-
- /*
- * TODO: maybe should have a native method that takes an array of
- * variable names and returns an array of values. But we'd still
- * be doing a round trip to the supplicant daemon for each variable.
- */
- String value;
-
- value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
- if (!TextUtils.isEmpty(value)) {
- config.SSID = value;
- } else {
- config.SSID = null;
- }
-
- value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
- if (!TextUtils.isEmpty(value)) {
- config.BSSID = value;
- } else {
- config.BSSID = null;
- }
-
- value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
- config.priority = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.priority = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
- config.hiddenSSID = false;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.hiddenSSID = Integer.parseInt(value) != 0;
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
- config.wepTxKeyIndex = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.wepTxKeyIndex = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- /*
- * Get up to 4 WEP keys. Note that the actual keys are not passed back,
- * just a "*" if the key is set, or the null string otherwise.
- */
- for (int i = 0; i < 4; i++) {
- value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepKeyVarNames[i]);
- if (!TextUtils.isEmpty(value)) {
- config.wepKeys[i] = value;
- } else {
- config.wepKeys[i] = null;
- }
- }
-
- /*
- * Get the private shared key. Note that the actual keys are not passed back,
- * just a "*" if the key is set, or the null string otherwise.
- */
- value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.pskVarName);
- if (!TextUtils.isEmpty(value)) {
- config.preSharedKey = value;
- } else {
- config.preSharedKey = null;
- }
-
- value = mWifiStateTracker.getNetworkVariable(config.networkId,
- WifiConfiguration.Protocol.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.Protocol.strings);
- if (0 <= index) {
- config.allowedProtocols.set(index);
- }
- }
- }
-
- value = mWifiStateTracker.getNetworkVariable(config.networkId,
- WifiConfiguration.KeyMgmt.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.KeyMgmt.strings);
- if (0 <= index) {
- config.allowedKeyManagement.set(index);
- }
- }
- }
-
- value = mWifiStateTracker.getNetworkVariable(config.networkId,
- WifiConfiguration.AuthAlgorithm.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
- if (0 <= index) {
- config.allowedAuthAlgorithms.set(index);
- }
- }
- }
-
- value = mWifiStateTracker.getNetworkVariable(config.networkId,
- WifiConfiguration.PairwiseCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.PairwiseCipher.strings);
- if (0 <= index) {
- config.allowedPairwiseCiphers.set(index);
- }
- }
- }
-
- value = mWifiStateTracker.getNetworkVariable(config.networkId,
- WifiConfiguration.GroupCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.GroupCipher.strings);
- if (0 <= index) {
- config.allowedGroupCiphers.set(index);
- }
- }
- }
-
- for (WifiConfiguration.EnterpriseField field :
- config.enterpriseFields) {
- value = mWifiStateTracker.getNetworkVariable(netId,
- field.varName());
- if (!TextUtils.isEmpty(value)) {
- if (field != config.eap) value = removeDoubleQuotes(value);
- field.setValue(value);
- }
- }
- }
-
- private static String removeDoubleQuotes(String string) {
- if (string.length() <= 2) return "";
- return string.substring(1, string.length() - 1);
- }
-
- private static String convertToQuotedString(String string) {
- return "\"" + string + "\"";
+ return mWifiStateMachine.syncGetConfiguredNetworks();
}
/**
@@ -1032,280 +618,15 @@ public class WifiService extends IWifiManager.Stub {
*/
public int addOrUpdateNetwork(WifiConfiguration config) {
enforceChangePermission();
-
- /*
- * If the supplied networkId is -1, we create a new empty
- * network configuration. Otherwise, the networkId should
- * refer to an existing configuration.
- */
- int netId = config.networkId;
- boolean newNetwork = netId == -1;
- boolean doReconfig = false;
- // networkId of -1 means we want to create a new network
- synchronized (mWifiStateTracker) {
- if (newNetwork) {
- netId = mWifiStateTracker.addNetwork();
- if (netId < 0) {
- if (DBG) {
- Slog.d(TAG, "Failed to add a network!");
- }
- return -1;
- }
- doReconfig = true;
- }
- mNeedReconfig = mNeedReconfig || doReconfig;
- }
-
- setVariables: {
- /*
- * Note that if a networkId for a non-existent network
- * was supplied, then the first setNetworkVariable()
- * will fail, so we don't bother to make a separate check
- * for the validity of the ID up front.
- */
- if (config.SSID != null &&
- !mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.ssidVarName,
- config.SSID)) {
- if (DBG) {
- Slog.d(TAG, "failed to set SSID: "+config.SSID);
- }
- break setVariables;
- }
-
- if (config.BSSID != null &&
- !mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.bssidVarName,
- config.BSSID)) {
- if (DBG) {
- Slog.d(TAG, "failed to set BSSID: "+config.BSSID);
- }
- break setVariables;
- }
-
- String allowedKeyManagementString =
- makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
- if (config.allowedKeyManagement.cardinality() != 0 &&
- !mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.KeyMgmt.varName,
- allowedKeyManagementString)) {
- if (DBG) {
- Slog.d(TAG, "failed to set key_mgmt: "+
- allowedKeyManagementString);
- }
- break setVariables;
- }
-
- String allowedProtocolsString =
- makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
- if (config.allowedProtocols.cardinality() != 0 &&
- !mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.Protocol.varName,
- allowedProtocolsString)) {
- if (DBG) {
- Slog.d(TAG, "failed to set proto: "+
- allowedProtocolsString);
- }
- break setVariables;
- }
-
- String allowedAuthAlgorithmsString =
- makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
- if (config.allowedAuthAlgorithms.cardinality() != 0 &&
- !mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.AuthAlgorithm.varName,
- allowedAuthAlgorithmsString)) {
- if (DBG) {
- Slog.d(TAG, "failed to set auth_alg: "+
- allowedAuthAlgorithmsString);
- }
- break setVariables;
- }
-
- String allowedPairwiseCiphersString =
- makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings);
- if (config.allowedPairwiseCiphers.cardinality() != 0 &&
- !mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.PairwiseCipher.varName,
- allowedPairwiseCiphersString)) {
- if (DBG) {
- Slog.d(TAG, "failed to set pairwise: "+
- allowedPairwiseCiphersString);
- }
- break setVariables;
- }
-
- String allowedGroupCiphersString =
- makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
- if (config.allowedGroupCiphers.cardinality() != 0 &&
- !mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.GroupCipher.varName,
- allowedGroupCiphersString)) {
- if (DBG) {
- Slog.d(TAG, "failed to set group: "+
- allowedGroupCiphersString);
- }
- break setVariables;
- }
-
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
- !mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.pskVarName,
- config.preSharedKey)) {
- if (DBG) {
- Slog.d(TAG, "failed to set psk: "+config.preSharedKey);
- }
- break setVariables;
- }
-
- boolean hasSetKey = false;
- if (config.wepKeys != null) {
- for (int i = 0; i < config.wepKeys.length; i++) {
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
- if (!mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.wepKeyVarNames[i],
- config.wepKeys[i])) {
- if (DBG) {
- Slog.d(TAG,
- "failed to set wep_key"+i+": " +
- config.wepKeys[i]);
- }
- break setVariables;
- }
- hasSetKey = true;
- }
- }
- }
-
- if (hasSetKey) {
- if (!mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.wepTxKeyIdxVarName,
- Integer.toString(config.wepTxKeyIndex))) {
- if (DBG) {
- Slog.d(TAG,
- "failed to set wep_tx_keyidx: "+
- config.wepTxKeyIndex);
- }
- break setVariables;
- }
- }
-
- if (!mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.priorityVarName,
- Integer.toString(config.priority))) {
- if (DBG) {
- Slog.d(TAG, config.SSID + ": failed to set priority: "
- +config.priority);
- }
- break setVariables;
- }
-
- if (config.hiddenSSID && !mWifiStateTracker.setNetworkVariable(
- netId,
- WifiConfiguration.hiddenSSIDVarName,
- Integer.toString(config.hiddenSSID ? 1 : 0))) {
- if (DBG) {
- Slog.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
- config.hiddenSSID);
- }
- break setVariables;
- }
-
- for (WifiConfiguration.EnterpriseField field
- : config.enterpriseFields) {
- String varName = field.varName();
- String value = field.value();
- if (value != null) {
- if (field != config.eap) {
- value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
- }
- if (!mWifiStateTracker.setNetworkVariable(
- netId,
- varName,
- value)) {
- if (DBG) {
- Slog.d(TAG, config.SSID + ": failed to set " + varName +
- ": " + value);
- }
- break setVariables;
- }
- }
- }
- return netId;
- }
-
- /*
- * For an update, if one of the setNetworkVariable operations fails,
- * we might want to roll back all the changes already made. But the
- * chances are that if anything is going to go wrong, it'll happen
- * the first time we try to set one of the variables.
- */
- if (newNetwork) {
- removeNetwork(netId);
- if (DBG) {
- Slog.d(TAG,
- "Failed to set a network variable, removed network: "
- + netId);
- }
- }
- return -1;
- }
-
- private static String makeString(BitSet set, String[] strings) {
- StringBuffer buf = new StringBuffer();
- int nextSetBit = -1;
-
- /* Make sure all set bits are in [0, strings.length) to avoid
- * going out of bounds on strings. (Shouldn't happen, but...) */
- set = set.get(0, strings.length);
-
- while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
- buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
- }
-
- // remove trailing space
- if (set.cardinality() > 0) {
- buf.setLength(buf.length() - 1);
- }
-
- return buf.toString();
- }
-
- private static int lookupString(String string, String[] strings) {
- int size = strings.length;
-
- string = string.replace('-', '_');
-
- for (int i = 0; i < size; i++)
- if (string.equals(strings[i]))
- return i;
-
- if (DBG) {
- // if we ever get here, we should probably add the
- // value to WifiConfiguration to reflect that it's
- // supported by the WPA supplicant
- Slog.w(TAG, "Failed to look-up a string: " + string);
+ if (mChannel != null) {
+ return mWifiStateMachine.syncAddOrUpdateNetwork(mChannel, config);
+ } else {
+ Slog.e(TAG, "mChannel is not initialized");
+ return -1;
}
-
- return -1;
}
- /**
+ /**
* See {@link android.net.wifi.WifiManager#removeNetwork(int)}
* @param netId the integer that identifies the network configuration
* to the supplicant
@@ -1313,8 +634,12 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean removeNetwork(int netId) {
enforceChangePermission();
-
- return mWifiStateTracker.removeNetwork(netId);
+ if (mChannel != null) {
+ return mWifiStateMachine.syncRemoveNetwork(mChannel, netId);
+ } else {
+ Slog.e(TAG, "mChannel is not initialized");
+ return false;
+ }
}
/**
@@ -1326,14 +651,12 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean enableNetwork(int netId, boolean disableOthers) {
enforceChangePermission();
-
- String ifname = mWifiStateTracker.getInterfaceName();
- NetworkUtils.enableInterface(ifname);
- boolean result = mWifiStateTracker.enableNetwork(netId, disableOthers);
- if (!result) {
- NetworkUtils.disableInterface(ifname);
+ if (mChannel != null) {
+ return mWifiStateMachine.syncEnableNetwork(mChannel, netId, disableOthers);
+ } else {
+ Slog.e(TAG, "mChannel is not initialized");
+ return false;
}
- return result;
}
/**
@@ -1344,8 +667,12 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean disableNetwork(int netId) {
enforceChangePermission();
-
- return mWifiStateTracker.disableNetwork(netId);
+ if (mChannel != null) {
+ return mWifiStateMachine.syncDisableNetwork(mChannel, netId);
+ } else {
+ Slog.e(TAG, "mChannel is not initialized");
+ return false;
+ }
}
/**
@@ -1358,7 +685,7 @@ public class WifiService extends IWifiManager.Stub {
* Make sure we have the latest information, by sending
* a status request to the supplicant.
*/
- return mWifiStateTracker.requestConnectionInfo();
+ return mWifiStateMachine.syncRequestConnectionInfo();
}
/**
@@ -1368,282 +695,161 @@ public class WifiService extends IWifiManager.Stub {
*/
public List<ScanResult> getScanResults() {
enforceAccessPermission();
- String reply;
+ return mWifiStateMachine.syncGetScanResultsList();
+ }
- reply = mWifiStateTracker.scanResults();
- if (reply == null) {
- return null;
+ /**
+ * Tell the supplicant to persist the current list of configured networks.
+ * @return {@code true} if the operation succeeded
+ *
+ * TODO: deprecate this
+ */
+ public boolean saveConfiguration() {
+ boolean result = true;
+ enforceChangePermission();
+ if (mChannel != null) {
+ return mWifiStateMachine.syncSaveConfig(mChannel);
+ } else {
+ Slog.e(TAG, "mChannel is not initialized");
+ return false;
}
+ }
- List<ScanResult> scanList = new ArrayList<ScanResult>();
-
- int lineCount = 0;
-
- int replyLen = reply.length();
- // Parse the result string, keeping in mind that the last line does
- // not end with a newline.
- for (int lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) {
- if (lineEnd == replyLen || reply.charAt(lineEnd) == '\n') {
- ++lineCount;
- /*
- * Skip the first line, which is a header
- */
- if (lineCount == 1) {
- lineBeg = lineEnd + 1;
- continue;
- }
- if (lineEnd > lineBeg) {
- String line = reply.substring(lineBeg, lineEnd);
- ScanResult scanResult = parseScanResult(line);
- if (scanResult != null) {
- scanList.add(scanResult);
- } else if (DBG) {
- Slog.w(TAG, "misformatted scan result for: " + line);
- }
- }
- lineBeg = lineEnd + 1;
- }
- }
- mWifiStateTracker.setScanResultsList(scanList);
- return scanList;
+ /**
+ * Set the country code
+ * @param countryCode ISO 3166 country code.
+ * @param persist {@code true} if the setting should be remembered.
+ *
+ * The persist behavior exists so that wifi can fall back to the last
+ * persisted country code on a restart, when the locale information is
+ * not available from telephony.
+ */
+ public void setCountryCode(String countryCode, boolean persist) {
+ Slog.i(TAG, "WifiService trying to set country code to " + countryCode +
+ " with persist set to " + persist);
+ enforceChangePermission();
+ mWifiStateMachine.setCountryCode(countryCode, persist);
}
/**
- * Parse the scan result line passed to us by wpa_supplicant (helper).
- * @param line the line to parse
- * @return the {@link ScanResult} object
+ * Set the operational frequency band
+ * @param band One of
+ * {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
+ * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
+ * {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
+ * @param persist {@code true} if the setting should be remembered.
+ *
*/
- private ScanResult parseScanResult(String line) {
- ScanResult scanResult = null;
- if (line != null) {
- /*
- * Cache implementation (LinkedHashMap) is not synchronized, thus,
- * must synchronized here!
- */
- synchronized (mScanResultCache) {
- String[] result = scanResultPattern.split(line);
- if (3 <= result.length && result.length <= 5) {
- String bssid = result[0];
- // bssid | frequency | level | flags | ssid
- int frequency;
- int level;
- try {
- frequency = Integer.parseInt(result[1]);
- level = Integer.parseInt(result[2]);
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (level > 0) level -= 256;
- } catch (NumberFormatException e) {
- frequency = 0;
- level = 0;
- }
+ public void setFrequencyBand(int band, boolean persist) {
+ enforceChangePermission();
+ if (!isDualBandSupported()) return;
+ Slog.i(TAG, "WifiService trying to set frequency band to " + band +
+ " with persist set to " + persist);
+ mWifiStateMachine.setFrequencyBand(band, persist);
+ }
- /*
- * The formatting of the results returned by
- * wpa_supplicant is intended to make the fields
- * line up nicely when printed,
- * not to make them easy to parse. So we have to
- * apply some heuristics to figure out which field
- * is the SSID and which field is the flags.
- */
- String ssid;
- String flags;
- if (result.length == 4) {
- if (result[3].charAt(0) == '[') {
- flags = result[3];
- ssid = "";
- } else {
- flags = "";
- ssid = result[3];
- }
- } else if (result.length == 5) {
- flags = result[3];
- ssid = result[4];
- } else {
- // Here, we must have 3 fields: no flags and ssid
- // set
- flags = "";
- ssid = "";
- }
- // bssid + ssid is the hash key
- String key = bssid + ssid;
- scanResult = mScanResultCache.get(key);
- if (scanResult != null) {
- scanResult.level = level;
- scanResult.SSID = ssid;
- scanResult.capabilities = flags;
- scanResult.frequency = frequency;
- } else {
- // Do not add scan results that have no SSID set
- if (0 < ssid.trim().length()) {
- scanResult =
- new ScanResult(
- ssid, bssid, flags, level, frequency);
- mScanResultCache.put(key, scanResult);
- }
- }
- } else {
- Slog.w(TAG, "Misformatted scan result text with " +
- result.length + " fields: " + line);
- }
- }
- }
+ /**
+ * Get the operational frequency band
+ */
+ public int getFrequencyBand() {
+ enforceAccessPermission();
+ return mWifiStateMachine.getFrequencyBand();
+ }
- return scanResult;
+ public boolean isDualBandSupported() {
+ //TODO: Should move towards adding a driver API that checks at runtime
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifi_dual_band_support);
}
/**
- * Parse the "flags" field passed back in a scan result by wpa_supplicant,
- * and construct a {@code WifiConfiguration} that describes the encryption,
- * key management, and authenticaion capabilities of the access point.
- * @param flags the string returned by wpa_supplicant
- * @return the {@link WifiConfiguration} object, filled in
+ * Return the DHCP-assigned addresses from the last successful DHCP request,
+ * if any.
+ * @return the DHCP information
*/
- WifiConfiguration parseScanFlags(String flags) {
- WifiConfiguration config = new WifiConfiguration();
-
- if (flags.length() == 0) {
- config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
- }
- // ... to be implemented
- return config;
+ public DhcpInfo getDhcpInfo() {
+ enforceAccessPermission();
+ return mWifiStateMachine.syncGetDhcpInfo();
}
/**
- * Tell the supplicant to persist the current list of configured networks.
- * @return {@code true} if the operation succeeded
+ * see {@link android.net.wifi.WifiManager#startWifi}
+ *
*/
- public boolean saveConfiguration() {
- boolean result;
+ public void startWifi() {
enforceChangePermission();
+ /* TODO: may be add permissions for access only to connectivity service
+ * TODO: if a start issued, keep wifi alive until a stop issued irrespective
+ * of WifiLock & device idle status unless wifi enabled status is toggled
+ */
- synchronized (mWifiStateTracker) {
- result = mWifiStateTracker.saveConfig();
- if (result && mNeedReconfig) {
- mNeedReconfig = false;
- result = mWifiStateTracker.reloadConfig();
-
- if (result) {
- Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION);
- mContext.sendBroadcast(intent);
- }
- }
- }
- // Inform the backup manager about a data change
- IBackupManager ibm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- if (ibm != null) {
- try {
- ibm.dataChanged("com.android.providers.settings");
- } catch (Exception e) {
- // Try again later
- }
- }
- return result;
+ mWifiStateMachine.setDriverStart(true);
+ mWifiStateMachine.reconnectCommand();
}
/**
- * Set the number of radio frequency channels that are allowed to be used
- * in the current regulatory domain. This method should be used only
- * if the correct number of channels cannot be determined automatically
- * for some reason. If the operation is successful, the new value may be
- * persisted as a Secure setting.
- * @param numChannels the number of allowed channels. Must be greater than 0
- * and less than or equal to 16.
- * @param persist {@code true} if the setting should be remembered.
- * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
- * {@code numChannels} is outside the valid range.
+ * see {@link android.net.wifi.WifiManager#stopWifi}
+ *
*/
- public boolean setNumAllowedChannels(int numChannels, boolean persist) {
- Slog.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
- " with persist set to "+persist);
+ public void stopWifi() {
enforceChangePermission();
-
- /*
- * Validate the argument. We'd like to let the Wi-Fi driver do this,
- * but if Wi-Fi isn't currently enabled, that's not possible, and
- * we want to persist the setting anyway,so that it will take
- * effect when Wi-Fi does become enabled.
+ /* TODO: may be add permissions for access only to connectivity service
+ * TODO: if a stop is issued, wifi is brought up only by startWifi
+ * unless wifi enabled status is toggled
*/
- boolean found = false;
- for (int validChan : sValidRegulatoryChannelCounts) {
- if (validChan == numChannels) {
- found = true;
- break;
- }
- }
- if (!found) {
- return false;
- }
+ mWifiStateMachine.setDriverStart(false);
+ }
- if (mWifiHandler == null) return false;
- Message.obtain(mWifiHandler,
- MESSAGE_SET_CHANNELS, numChannels, (persist ? 1 : 0)).sendToTarget();
+ /**
+ * see {@link android.net.wifi.WifiManager#addToBlacklist}
+ *
+ */
+ public void addToBlacklist(String bssid) {
+ enforceChangePermission();
- return true;
+ mWifiStateMachine.addToBlacklist(bssid);
}
/**
- * sets the number of allowed radio frequency channels synchronously
- * @param numChannels the number of allowed channels. Must be greater than 0
- * and less than or equal to 16.
- * @param persist {@code true} if the setting should be remembered.
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ * see {@link android.net.wifi.WifiManager#clearBlacklist}
+ *
*/
- private boolean setNumAllowedChannelsBlocking(int numChannels, boolean persist) {
- if (persist) {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
- numChannels);
- }
- return mWifiStateTracker.setNumAllowedChannels(numChannels);
+ public void clearBlacklist() {
+ enforceChangePermission();
+
+ mWifiStateMachine.clearBlacklist();
}
- /**
- * Return the number of frequency channels that are allowed
- * to be used in the current regulatory domain.
- * @return the number of allowed channels, or {@code -1} if an error occurs
- */
- public int getNumAllowedChannels() {
- int numChannels;
+ public void connectNetworkWithId(int networkId) {
+ enforceChangePermission();
+ mWifiStateMachine.connectNetwork(networkId);
+ }
- enforceAccessPermission();
+ public void connectNetworkWithConfig(WifiConfiguration config) {
+ enforceChangePermission();
+ mWifiStateMachine.connectNetwork(config);
+ }
- /*
- * If we can't get the value from the driver (e.g., because
- * Wi-Fi is not currently enabled), get the value from
- * Settings.
- */
- numChannels = mWifiStateTracker.getNumAllowedChannels();
- if (numChannels < 0) {
- numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
- -1);
- }
- return numChannels;
+ public void saveNetwork(WifiConfiguration config) {
+ enforceChangePermission();
+ mWifiStateMachine.saveNetwork(config);
}
- /**
- * Return the list of valid values for the number of allowed radio channels
- * for various regulatory domains.
- * @return the list of channel counts
- */
- public int[] getValidChannelCounts() {
- enforceAccessPermission();
- return sValidRegulatoryChannelCounts;
+ public void forgetNetwork(int netId) {
+ enforceChangePermission();
+ mWifiStateMachine.forgetNetwork(netId);
}
- /**
- * Return the DHCP-assigned addresses from the last successful DHCP request,
- * if any.
- * @return the DHCP information
- */
- public DhcpInfo getDhcpInfo() {
- enforceAccessPermission();
- return mWifiStateTracker.getDhcpInfo();
+ public WpsResult startWps(WpsConfiguration config) {
+ enforceChangePermission();
+ if (mChannel != null) {
+ return mWifiStateMachine.startWps(mChannel, config);
+ } else {
+ Slog.e(TAG, "mChannel is not initialized");
+ return new WpsResult(WpsResult.Status.FAILURE);
+ }
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -1653,7 +859,7 @@ public class WifiService extends IWifiManager.Stub {
long idleMillis =
Settings.Secure.getLong(mContext.getContentResolver(),
- Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
+ Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
int stayAwakeConditions =
Settings.System.getInt(mContext.getContentResolver(),
Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
@@ -1666,20 +872,16 @@ public class WifiService extends IWifiManager.Stub {
mScreenOff = false;
// Once the screen is on, we are not keeping WIFI running
// because of any locks so clear that tracking immediately.
- sendReportWorkSourceMessage();
- sendEnableRssiPollingMessage(true);
- /* DHCP or other temporary failures in the past can prevent
- * a disabled network from being connected to, enable on screen on
- */
- if (mWifiStateTracker.isAnyNetworkDisabled()) {
- sendEnableNetworksMessage();
- }
+ reportStartWorkSource();
+ mWifiStateMachine.enableRssiPolling(true);
+ mWifiStateMachine.enableAllNetworks();
+ updateWifiState();
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
if (DBG) {
Slog.d(TAG, "ACTION_SCREEN_OFF");
}
mScreenOff = true;
- sendEnableRssiPollingMessage(false);
+ mWifiStateMachine.enableRssiPolling(false);
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
* AND the "stay on while plugged in" setting doesn't match the
@@ -1687,11 +889,11 @@ public class WifiService extends IWifiManager.Stub {
* or plugged in to AC).
*/
if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
- WifiInfo info = mWifiStateTracker.requestConnectionInfo();
+ WifiInfo info = mWifiStateMachine.syncRequestConnectionInfo();
if (info.getSupplicantState() != SupplicantState.COMPLETED) {
// we used to go to sleep immediately, but this caused some race conditions
- // we don't have time to track down for this release. Delay instead, but not
- // as long as we would if connected (below)
+ // we don't have time to track down for this release. Delay instead,
+ // but not as long as we would if connected (below)
// TODO - fix the race conditions and switch back to the immediate turn-off
long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
if (DBG) {
@@ -1710,14 +912,13 @@ public class WifiService extends IWifiManager.Stub {
mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
}
}
- /* we can return now -- there's nothing to do until we get the idle intent back */
- return;
} else if (action.equals(ACTION_DEVICE_IDLE)) {
if (DBG) {
Slog.d(TAG, "got ACTION_DEVICE_IDLE");
}
mDeviceIdle = true;
- sendReportWorkSourceMessage();
+ reportStartWorkSource();
+ updateWifiState();
} else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
@@ -1737,26 +938,13 @@ public class WifiService extends IWifiManager.Stub {
Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
}
mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
- mPluggedType = pluggedType;
- return;
}
mPluggedType = pluggedType;
- } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
- BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
- Set<BluetoothDevice> sinks = a2dp.getConnectedSinks();
- boolean isBluetoothPlaying = false;
- for (BluetoothDevice sink : sinks) {
- if (a2dp.getSinkState(sink) == BluetoothA2dp.STATE_PLAYING) {
- isBluetoothPlaying = true;
- }
- }
- mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
-
- } else {
- return;
+ } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
+ BluetoothAdapter.STATE_DISCONNECTED);
+ mWifiStateMachine.sendBluetoothAdapterStateChange(state);
}
-
- updateWifiState();
}
/**
@@ -1767,8 +955,10 @@ public class WifiService extends IWifiManager.Stub {
* @see #shouldDeviceStayAwake(int, int)
*/
private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
+ //Never sleep when plugged in as long as the user has not changed the settings
int wifiSleepPolicy = Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
+ Settings.System.WIFI_SLEEP_POLICY,
+ Settings.System.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED);
if (wifiSleepPolicy == Settings.System.WIFI_SLEEP_POLICY_NEVER) {
// Never sleep
@@ -1789,7 +979,7 @@ public class WifiService extends IWifiManager.Stub {
* of {@code 0} isn't really a plugged type, but rather an indication that the
* device isn't plugged in at all, there is no bit value corresponding to a
* {@code pluggedType} value of {@code 0}. That is why we shift by
- * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
+ * {@code pluggedType - 1} instead of by {@code pluggedType}.
* @param stayAwakeConditions a bit string specifying which "plugged types" should
* keep the device (and hence Wi-Fi) awake.
* @param pluggedType the type of plug (USB, AC, or none) for which the check is
@@ -1802,63 +992,20 @@ public class WifiService extends IWifiManager.Stub {
}
};
- private void sendEnableMessage(boolean enable, boolean persist, int uid) {
- Message msg = Message.obtain(mWifiHandler,
- (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
- (persist ? 1 : 0), uid);
- msg.sendToTarget();
- }
-
- private void sendStartMessage(int lockMode) {
- Message.obtain(mWifiHandler, MESSAGE_START_WIFI, lockMode, 0).sendToTarget();
- }
-
- private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
- Message.obtain(mWifiHandler,
- (enable ? MESSAGE_START_ACCESS_POINT : MESSAGE_STOP_ACCESS_POINT),
- uid, 0, wifiConfig).sendToTarget();
- }
-
- private void sendEnableNetworksMessage() {
- Message.obtain(mWifiHandler, MESSAGE_ENABLE_NETWORKS).sendToTarget();
- }
-
- private void sendReportWorkSourceMessage() {
- Message.obtain(mWifiHandler, MESSAGE_REPORT_WORKSOURCE).sendToTarget();
- }
-
- private void sendEnableRssiPollingMessage(boolean enable) {
- Message.obtain(mWifiHandler, MESSAGE_ENABLE_RSSI_POLLING, enable ? 1 : 0, 0).sendToTarget();
- }
-
-
- private void reportStartWorkSource() {
- synchronized (mWifiStateTracker) {
- mTmpWorkSource.clear();
- if (mDeviceIdle) {
- for (int i=0; i<mLocks.mList.size(); i++) {
- mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
- }
+ private synchronized void reportStartWorkSource() {
+ mTmpWorkSource.clear();
+ if (mDeviceIdle) {
+ for (int i=0; i<mLocks.mList.size(); i++) {
+ mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
}
- mWifiStateTracker.updateBatteryWorkSourceLocked(mTmpWorkSource);
- sWakeLock.setWorkSource(mTmpWorkSource);
}
+ mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
}
private void updateWifiState() {
- // send a message so it's all serialized
- Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
- }
-
- private void doUpdateWifiState() {
boolean wifiEnabled = getPersistedWifiEnabled();
- boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
-
- boolean lockHeld;
- synchronized (mLocks) {
- lockHeld = mLocks.hasLocks();
- }
-
+ boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden.get();
+ boolean lockHeld = mLocks.hasLocks();
int strongestLockMode = WifiManager.WIFI_MODE_FULL;
boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
@@ -1871,43 +1018,26 @@ public class WifiService extends IWifiManager.Stub {
strongestLockMode = WifiManager.WIFI_MODE_FULL;
}
- synchronized (mWifiHandler) {
- if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLING) && !airplaneMode) {
- return;
- }
-
- /* Disable tethering when airplane mode is enabled */
- if (airplaneMode &&
- (mWifiApState == WIFI_AP_STATE_ENABLING || mWifiApState == WIFI_AP_STATE_ENABLED)) {
- sWakeLock.acquire();
- sendAccessPointMessage(false, null, mLastApEnableUid);
- }
+ /* Disable tethering when airplane mode is enabled */
+ if (airplaneMode) {
+ mWifiStateMachine.setWifiApEnabled(null, false);
+ }
- if (wifiShouldBeEnabled) {
- if (wifiShouldBeStarted) {
- sWakeLock.acquire();
- sendEnableMessage(true, false, mLastEnableUid);
- sWakeLock.acquire();
- sendStartMessage(strongestLockMode);
- } else if (!mWifiStateTracker.isDriverStopped()) {
- int wakeLockTimeout =
- Settings.Secure.getInt(
- mContext.getContentResolver(),
- Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
- DEFAULT_WAKELOCK_TIMEOUT);
- /*
- * We are assuming that ConnectivityService can make
- * a transition to cellular data within wakeLockTimeout time.
- * The wakelock is released by the delayed message.
- */
- sDriverStopWakeLock.acquire();
- mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
- mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
- }
+ if (wifiShouldBeEnabled) {
+ if (wifiShouldBeStarted) {
+ reportStartWorkSource();
+ mWifiStateMachine.setWifiEnabled(true);
+ mWifiStateMachine.setScanOnlyMode(
+ strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
+ mWifiStateMachine.setDriverStart(true);
+ mWifiStateMachine.setHighPerfModeEnabled(strongestLockMode
+ == WifiManager.WIFI_MODE_FULL_HIGH_PERF);
} else {
- sWakeLock.acquire();
- sendEnableMessage(false, false, mLastEnableUid);
+ mWifiStateMachine.requestCmWakeLock();
+ mWifiStateMachine.setDriverStart(false);
}
+ } else {
+ mWifiStateMachine.setWifiEnabled(false);
}
}
@@ -1917,7 +1047,7 @@ public class WifiService extends IWifiManager.Stub {
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
intentFilter.addAction(ACTION_DEVICE_IDLE);
- intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+ intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
mContext.registerReceiver(mReceiver, intentFilter);
}
@@ -1945,103 +1075,6 @@ public class WifiService extends IWifiManager.Stub {
Settings.System.AIRPLANE_MODE_ON, 0) == 1;
}
- /**
- * Handler that allows posting to the WifiThread.
- */
- private class WifiHandler extends Handler {
- public WifiHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
-
- case MESSAGE_ENABLE_WIFI:
- setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2);
- if (mWifiWatchdogService == null) {
- mWifiWatchdogService = new WifiWatchdogService(mContext, mWifiStateTracker);
- }
- sWakeLock.release();
- break;
-
- case MESSAGE_START_WIFI:
- reportStartWorkSource();
- mWifiStateTracker.setScanOnlyMode(msg.arg1 == WifiManager.WIFI_MODE_SCAN_ONLY);
- mWifiStateTracker.restart();
- mWifiStateTracker.setHighPerfMode(msg.arg1 ==
- WifiManager.WIFI_MODE_FULL_HIGH_PERF);
- sWakeLock.release();
- break;
-
- case MESSAGE_UPDATE_STATE:
- doUpdateWifiState();
- break;
-
- case MESSAGE_DISABLE_WIFI:
- // a non-zero msg.arg1 value means the "enabled" setting
- // should be persisted
- setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2);
- mWifiWatchdogService = null;
- sWakeLock.release();
- break;
-
- case MESSAGE_STOP_WIFI:
- mWifiStateTracker.disconnectAndStop();
- // don't release wakelock
- break;
-
- case MESSAGE_RELEASE_WAKELOCK:
- sDriverStopWakeLock.release();
- break;
-
- case MESSAGE_START_ACCESS_POINT:
- setWifiApEnabledBlocking(true,
- msg.arg1,
- (WifiConfiguration) msg.obj);
- sWakeLock.release();
- break;
-
- case MESSAGE_STOP_ACCESS_POINT:
- setWifiApEnabledBlocking(false,
- msg.arg1,
- (WifiConfiguration) msg.obj);
- sWakeLock.release();
- break;
-
- case MESSAGE_SET_CHANNELS:
- setNumAllowedChannelsBlocking(msg.arg1, msg.arg2 == 1);
- break;
-
- case MESSAGE_ENABLE_NETWORKS:
- mWifiStateTracker.enableAllNetworks(getConfiguredNetworks());
- break;
-
- case MESSAGE_START_SCAN:
- boolean forceActive = (msg.arg1 == 1);
- switch (mWifiStateTracker.getSupplicantState()) {
- case DISCONNECTED:
- case INACTIVE:
- case SCANNING:
- case DORMANT:
- break;
- default:
- mWifiStateTracker.setScanResultHandling(
- WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
- break;
- }
- mWifiStateTracker.scan(forceActive);
- break;
- case MESSAGE_REPORT_WORKSOURCE:
- reportStartWorkSource();
- break;
- case MESSAGE_ENABLE_RSSI_POLLING:
- mWifiStateTracker.enableRssiPolling(msg.arg1 == 1);
- break;
- }
- }
- }
-
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -2051,17 +1084,17 @@ public class WifiService extends IWifiManager.Stub {
+ ", uid=" + Binder.getCallingUid());
return;
}
- pw.println("Wi-Fi is " + stateName(mWifiStateTracker.getWifiState()));
+ pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
pw.println("Stay-awake conditions: " +
Settings.System.getInt(mContext.getContentResolver(),
Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
pw.println();
pw.println("Internal state:");
- pw.println(mWifiStateTracker);
+ pw.println(mWifiStateMachine);
pw.println();
pw.println("Latest scan results:");
- List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
+ List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
if (scanResults != null && scanResults.size() != 0) {
pw.println(" BSSID Frequency RSSI Flags SSID");
for (ScanResult r : scanResults) {
@@ -2085,23 +1118,6 @@ public class WifiService extends IWifiManager.Stub {
mLocks.dump(pw);
}
- private static String stateName(int wifiState) {
- switch (wifiState) {
- case WIFI_STATE_DISABLING:
- return "disabling";
- case WIFI_STATE_DISABLED:
- return "disabled";
- case WIFI_STATE_ENABLING:
- return "enabling";
- case WIFI_STATE_ENABLED:
- return "enabled";
- case WIFI_STATE_UNKNOWN:
- return "unknown state";
- default:
- return "[invalid state]";
- }
- }
-
private class WifiLock extends DeathRecipient {
WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
super(lockMode, tag, binder, ws);
@@ -2179,7 +1195,7 @@ public class WifiService extends IWifiManager.Stub {
}
void enforceWakeSourcePermission(int uid, int pid) {
- if (uid == Process.myUid()) {
+ if (uid == android.os.Process.myUid()) {
return;
}
mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
@@ -2213,10 +1229,7 @@ public class WifiService extends IWifiManager.Stub {
private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
- mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
- break;
case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
- /* Treat high power as a full lock for battery stats */
mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
@@ -2228,10 +1241,7 @@ public class WifiService extends IWifiManager.Stub {
private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
- mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
- break;
case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
- /* Treat high power as a full lock for battery stats */
mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
@@ -2255,6 +1265,7 @@ public class WifiService extends IWifiManager.Stub {
case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
++mFullHighPerfLocksAcquired;
break;
+
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksAcquired;
break;
@@ -2262,7 +1273,7 @@ public class WifiService extends IWifiManager.Stub {
// Be aggressive about adding new locks into the accounted state...
// we want to over-report rather than under-report.
- sendReportWorkSourceMessage();
+ reportStartWorkSource();
updateWifiState();
return true;
@@ -2401,7 +1412,7 @@ public class WifiService extends IWifiManager.Stub {
if (mMulticasters.size() != 0) {
return;
} else {
- mWifiStateTracker.startPacketFiltering();
+ mWifiStateMachine.startPacketFiltering();
}
}
}
@@ -2416,7 +1427,7 @@ public class WifiService extends IWifiManager.Stub {
// our new size == 1 (first call), but this function won't
// be called often and by making the stopPacket call each
// time we're less fragile and self-healing.
- mWifiStateTracker.stopPacketFiltering();
+ mWifiStateMachine.stopPacketFiltering();
}
int uid = Binder.getCallingUid();
@@ -2453,7 +1464,7 @@ public class WifiService extends IWifiManager.Stub {
removed.unlinkDeathRecipient();
}
if (mMulticasters.size() == 0) {
- mWifiStateTracker.startPacketFiltering();
+ mWifiStateMachine.startPacketFiltering();
}
Long ident = Binder.clearCallingIdentity();
@@ -2472,4 +1483,144 @@ public class WifiService extends IWifiManager.Stub {
return (mMulticasters.size() > 0);
}
}
+
+ private void checkAndSetNotification() {
+ // If we shouldn't place a notification on available networks, then
+ // don't bother doing any of the following
+ if (!mNotificationEnabled) return;
+
+ State state = mNetworkInfo.getState();
+ if ((state == NetworkInfo.State.DISCONNECTED)
+ || (state == NetworkInfo.State.UNKNOWN)) {
+ // Look for an open network
+ List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
+ if (scanResults != null) {
+ int numOpenNetworks = 0;
+ for (int i = scanResults.size() - 1; i >= 0; i--) {
+ ScanResult scanResult = scanResults.get(i);
+
+ if (TextUtils.isEmpty(scanResult.capabilities)) {
+ numOpenNetworks++;
+ }
+ }
+
+ if (numOpenNetworks > 0) {
+ if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
+ /*
+ * We've scanned continuously at least
+ * NUM_SCANS_BEFORE_NOTIFICATION times. The user
+ * probably does not have a remembered network in range,
+ * since otherwise supplicant would have tried to
+ * associate and thus resetting this counter.
+ */
+ setNotificationVisible(true, numOpenNetworks, false, 0);
+ }
+ return;
+ }
+ }
+ }
+
+ // No open networks in range, remove the notification
+ setNotificationVisible(false, 0, false, 0);
+ }
+
+ /**
+ * Clears variables related to tracking whether a notification has been
+ * shown recently and clears the current notification.
+ */
+ private void resetNotification() {
+ mNotificationRepeatTime = 0;
+ mNumScansSinceNetworkStateChange = 0;
+ setNotificationVisible(false, 0, false, 0);
+ }
+
+ /**
+ * Display or don't display a notification that there are open Wi-Fi networks.
+ * @param visible {@code true} if notification should be visible, {@code false} otherwise
+ * @param numNetworks the number networks seen
+ * @param force {@code true} to force notification to be shown/not-shown,
+ * even if it is already shown/not-shown.
+ * @param delay time in milliseconds after which the notification should be made
+ * visible or invisible.
+ */
+ private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
+ int delay) {
+
+ // Since we use auto cancel on the notification, when the
+ // mNetworksAvailableNotificationShown is true, the notification may
+ // have actually been canceled. However, when it is false we know
+ // for sure that it is not being shown (it will not be shown any other
+ // place than here)
+
+ // If it should be hidden and it is already hidden, then noop
+ if (!visible && !mNotificationShown && !force) {
+ return;
+ }
+
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ Message message;
+ if (visible) {
+
+ // Not enough time has passed to show the notification again
+ if (System.currentTimeMillis() < mNotificationRepeatTime) {
+ return;
+ }
+
+ if (mNotification == null) {
+ // Cache the Notification object.
+ mNotification = new Notification();
+ mNotification.when = 0;
+ mNotification.icon = ICON_NETWORKS_AVAILABLE;
+ mNotification.flags = Notification.FLAG_AUTO_CANCEL;
+ mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
+ }
+
+ CharSequence title = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available, numNetworks);
+ CharSequence details = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
+ mNotification.tickerText = title;
+ mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
+
+ mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
+
+ notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification);
+ } else {
+ notificationManager.cancel(ICON_NETWORKS_AVAILABLE);
+ }
+
+ mNotificationShown = visible;
+ }
+
+ private class NotificationEnabledSettingObserver extends ContentObserver {
+
+ public NotificationEnabledSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register() {
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
+ mNotificationEnabled = getValue();
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ mNotificationEnabled = getValue();
+ resetNotification();
+ }
+
+ private boolean getValue() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
+ }
+ }
+
+
}
diff --git a/services/java/com/android/server/WifiStateTracker.java b/services/java/com/android/server/WifiStateTracker.java
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/java/com/android/server/WifiStateTracker.java
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 445dd03..46d6bef 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -27,7 +27,6 @@ import android.net.DhcpInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateTracker;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -77,7 +76,6 @@ public class WifiWatchdogService {
private Context mContext;
private ContentResolver mContentResolver;
- private WifiStateTracker mWifiStateTracker;
private WifiManager mWifiManager;
/**
@@ -108,10 +106,9 @@ public class WifiWatchdogService {
/** Whether the current AP check should be canceled. */
private boolean mShouldCancel;
- WifiWatchdogService(Context context, WifiStateTracker wifiStateTracker) {
+ WifiWatchdogService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
- mWifiStateTracker = wifiStateTracker;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
createThread();
@@ -275,12 +272,13 @@ public class WifiWatchdogService {
/**
* Unregister broadcasts and quit the watchdog thread
*/
- private void quit() {
- unregisterForWifiBroadcasts();
- mContext.getContentResolver().unregisterContentObserver(mContentObserver);
- mHandler.removeAllActions();
- mHandler.getLooper().quit();
- }
+ //TODO: Change back to running WWS when needed
+// private void quit() {
+// unregisterForWifiBroadcasts();
+// mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+// mHandler.removeAllActions();
+// mHandler.getLooper().quit();
+// }
/**
* Waits for the main watchdog thread to create the handler.
@@ -751,7 +749,7 @@ public class WifiWatchdogService {
// Black list this "bad" AP, this will cause an attempt to connect to another
blacklistAp(ap.bssid);
// Initiate an association to an alternate AP
- mWifiStateTracker.reassociate();
+ mWifiManager.reassociate();
}
private void blacklistAp(String bssid) {
@@ -762,10 +760,7 @@ public class WifiWatchdogService {
// Before taking action, make sure we should not cancel our processing
if (shouldCancel()) return;
- if (!mWifiStateTracker.addToBlacklist(bssid)) {
- // There's a known bug where this method returns failure on success
- //Slog.e(TAG, "Blacklisting " + bssid + " failed");
- }
+ mWifiManager.addToBlacklist(bssid);
if (D) {
myLogD("Blacklisting " + bssid);
@@ -860,10 +855,7 @@ public class WifiWatchdogService {
* (and blacklisted them). Clear the blacklist so the AP with best
* signal is chosen.
*/
- if (!mWifiStateTracker.clearBlacklist()) {
- // There's a known bug where this method returns failure on success
- //Slog.e(TAG, "Clearing blacklist failed");
- }
+ mWifiManager.clearBlacklist();
if (V) {
myLogV("handleSleep: Set state to SLEEP and cleared blacklist");
@@ -934,7 +926,7 @@ public class WifiWatchdogService {
* should revert anything done by the watchdog monitoring.
*/
private void handleReset() {
- mWifiStateTracker.clearBlacklist();
+ mWifiManager.clearBlacklist();
setIdleState(true);
}
@@ -1151,7 +1143,7 @@ public class WifiWatchdogService {
private void handleWifiStateChanged(int wifiState) {
if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
- quit();
+ onDisconnected();
} else if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
onEnabled();
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index f28bae0..eeb224c 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -23,13 +23,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_PUSH_BUFFERS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -39,6 +37,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
import com.android.internal.policy.impl.PhoneWindowManager;
+import com.android.internal.view.BaseInputHandler;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
@@ -50,6 +49,8 @@ import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -57,10 +58,13 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Typeface;
@@ -81,6 +85,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.TokenWatcher;
@@ -92,8 +97,8 @@ import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.view.Display;
+import android.view.DragEvent;
import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
import android.view.IApplicationToken;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
@@ -103,6 +108,8 @@ import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.InputHandler;
+import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
@@ -119,7 +126,6 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
-import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
@@ -143,6 +149,7 @@ public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
+ static final boolean DEBUG_ADD_REMOVE = false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
@@ -152,13 +159,14 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
static final boolean DEBUG_WINDOW_MOVEMENT = false;
+ static final boolean DEBUG_TOKEN_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_CONFIGURATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
- static final boolean DEBUG_FREEZE = false;
+ static final boolean DEBUG_DRAG = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
@@ -195,13 +203,19 @@ public class WindowManagerService extends IWindowManager.Stub
*/
static final int DEFAULT_FADE_IN_OUT_DURATION = 400;
- /** Adjustment to time to perform a dim, to make it more dramatic.
+ /**
+ * If true, the window manager will do its own custom freezing and general
+ * management of the screen during rotation.
*/
- static final int DIM_DURATION_MULTIPLIER = 6;
-
+ static final boolean CUSTOM_SCREEN_ROTATION = true;
+
// Maximum number of milliseconds to wait for input event injection.
// FIXME is this value reasonable?
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
+
+ // Maximum number of milliseconds to wait for input devices to be enumerated before
+ // proceding with safe mode detection.
+ private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000;
// Default input dispatching timeout in nanoseconds.
private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
@@ -288,12 +302,6 @@ public class WindowManagerService extends IWindowManager.Stub
new HashMap<IBinder, WindowToken>();
/**
- * The same tokens as mTokenMap, stored in a list for efficient iteration
- * over them.
- */
- final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>();
-
- /**
* Window tokens that are in the process of exiting, but still
* on screen for animations.
*/
@@ -302,7 +310,7 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Z-ordered (bottom-most first) list of all application tokens, for
* controlling the ordering of windows in different applications. This
- * contains WindowToken objects.
+ * contains AppWindowToken objects.
*/
final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();
@@ -319,18 +327,6 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>();
/**
- * This was the app token that was used to retrieve the last enter
- * animation. It will be used for the next exit animation.
- */
- AppWindowToken mLastEnterAnimToken;
-
- /**
- * These were the layout params used to retrieve the last enter animation.
- * They will be used for the next exit animation.
- */
- LayoutParams mLastEnterAnimParams;
-
- /**
* Z-ordered (bottom-most first) list of all Window objects.
*/
final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
@@ -348,6 +344,11 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<WindowState> mPendingRemove = new ArrayList<WindowState>();
/**
+ * Used when processing mPendingRemove to avoid working on the original array.
+ */
+ WindowState[] mPendingRemoveTmp = new WindowState[20];
+
+ /**
* Windows whose surface should be destroyed.
*/
final ArrayList<WindowState> mDestroySurface = new ArrayList<WindowState>();
@@ -364,6 +365,12 @@ public class WindowManagerService extends IWindowManager.Stub
*/
ArrayList<WindowState> mForceRemoves;
+ /**
+ * Used when rebuilding window list to keep track of windows that have
+ * been removed.
+ */
+ WindowState[] mRebuildTmp = new WindowState[20];
+
IInputMethodManager mInputMethodManager;
SurfaceSession mFxSession;
@@ -371,6 +378,8 @@ public class WindowManagerService extends IWindowManager.Stub
Surface mBlurSurface;
boolean mBlurShown;
Watermark mWatermark;
+ StrictModeFlash mStrictModeFlash;
+ ScreenRotationAnimation mScreenRotationAnimation;
int mTransactionSequence = 0;
@@ -387,6 +396,8 @@ public class WindowManagerService extends IWindowManager.Stub
int mLastRotationFlags;
ArrayList<IRotationWatcher> mRotationWatchers
= new ArrayList<IRotationWatcher>();
+ int mDeferredRotation;
+ int mDeferredRotationAnimFlags;
boolean mLayoutNeeded = true;
boolean mAnimationPending = false;
@@ -437,7 +448,6 @@ public class WindowManagerService extends IWindowManager.Stub
// This just indicates the window the input method is on top of, not
// necessarily the window its input is going to.
WindowState mInputMethodTarget = null;
- WindowState mUpcomingInputMethodTarget = null;
boolean mInputMethodTargetWaitingAnim;
int mInputMethodAnimLayerAdjustment;
@@ -455,6 +465,11 @@ public class WindowManagerService extends IWindowManager.Stub
// If non-null, we are in the middle of animating from one wallpaper target
// to another, and this is the higher one in Z-order.
WindowState mUpperWallpaperTarget = null;
+ // Window currently running an animation that has requested it be detached
+ // from the wallpaper. This means we need to ensure the wallpaper is
+ // visible behind it in case it animates in a way that would allow it to be
+ // seen.
+ WindowState mWindowDetachedWallpaper = null;
int mWallpaperAnimLayerAdjustment;
float mLastWallpaperX = -1;
float mLastWallpaperY = -1;
@@ -486,6 +501,390 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mTurnOnScreen;
/**
+ * Drag/drop state
+ */
+ class DragState {
+ IBinder mToken;
+ Surface mSurface;
+ int mFlags;
+ IBinder mLocalWin;
+ ClipData mData;
+ ClipDescription mDataDescription;
+ boolean mDragResult;
+ float mCurrentX, mCurrentY;
+ float mThumbOffsetX, mThumbOffsetY;
+ InputChannel mServerChannel, mClientChannel;
+ WindowState mTargetWindow;
+ ArrayList<WindowState> mNotifiedWindows;
+ boolean mDragInProgress;
+
+ private final Region mTmpRegion = new Region();
+
+ DragState(IBinder token, Surface surface, int flags, IBinder localWin) {
+ mToken = token;
+ mSurface = surface;
+ mFlags = flags;
+ mLocalWin = localWin;
+ mNotifiedWindows = new ArrayList<WindowState>();
+ }
+
+ void reset() {
+ if (mSurface != null) {
+ mSurface.destroy();
+ }
+ mSurface = null;
+ mFlags = 0;
+ mLocalWin = null;
+ mToken = null;
+ mData = null;
+ mThumbOffsetX = mThumbOffsetY = 0;
+ mNotifiedWindows = null;
+ }
+
+ void register() {
+ if (DEBUG_DRAG) Slog.d(TAG, "registering drag input channel");
+ if (mClientChannel != null) {
+ Slog.e(TAG, "Duplicate register of drag input channel");
+ } else {
+ InputChannel[] channels = InputChannel.openInputChannelPair("drag");
+ mServerChannel = channels[0];
+ mClientChannel = channels[1];
+ mInputManager.registerInputChannel(mServerChannel, null);
+ InputQueue.registerInputChannel(mClientChannel, mDragInputHandler,
+ mH.getLooper().getQueue());
+ }
+ }
+
+ void unregister() {
+ if (DEBUG_DRAG) Slog.d(TAG, "unregistering drag input channel");
+ if (mClientChannel == null) {
+ Slog.e(TAG, "Unregister of nonexistent drag input channel");
+ } else {
+ mInputManager.unregisterInputChannel(mServerChannel);
+ InputQueue.unregisterInputChannel(mClientChannel);
+ mClientChannel.dispose();
+ mServerChannel.dispose();
+ mClientChannel = null;
+ mServerChannel = null;
+ }
+ }
+
+ int getDragLayerLw() {
+ return mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
+ * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
+ }
+
+ /* call out to each visible window/session informing it about the drag
+ */
+ void broadcastDragStartedLw(final float touchX, final float touchY) {
+ // Cache a base-class instance of the clip metadata so that parceling
+ // works correctly in calling out to the apps.
+ mDataDescription = (mData != null) ? mData.getDescription() : null;
+ mNotifiedWindows.clear();
+ mDragInProgress = true;
+
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
+ }
+
+ final int N = mWindows.size();
+ for (int i = 0; i < N; i++) {
+ sendDragStartedLw(mWindows.get(i), touchX, touchY, mDataDescription);
+ }
+ }
+
+ /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
+ * designated window is potentially a drop recipient. There are race situations
+ * around DRAG_ENDED broadcast, so we make sure that once we've declared that
+ * the drag has ended, we never send out another DRAG_STARTED for this drag action.
+ *
+ * This method clones the 'event' parameter if it's being delivered to the same
+ * process, so it's safe for the caller to call recycle() on the event afterwards.
+ */
+ private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
+ ClipDescription desc) {
+ // Don't actually send the event if the drag is supposed to be pinned
+ // to the originating window but 'newWin' is not that window.
+ if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
+ final IBinder winBinder = newWin.mClient.asBinder();
+ if (winBinder != mLocalWin) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "Not dispatching local DRAG_STARTED to " + newWin);
+ }
+ return;
+ }
+ }
+
+ if (mDragInProgress && newWin.isPotentialDragTarget()) {
+ DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED,
+ touchX - newWin.mFrame.left, touchY - newWin.mFrame.top,
+ null, desc, null, false);
+ try {
+ newWin.mClient.dispatchDragEvent(event);
+ // track each window that we've notified that the drag is starting
+ mNotifiedWindows.add(newWin);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to drag-start window " + newWin);
+ } finally {
+ // if the callee was local, the dispatch has already recycled the event
+ if (Process.myPid() != newWin.mSession.mPid) {
+ event.recycle();
+ }
+ }
+ }
+ }
+
+ /* helper - construct and send a DRAG_STARTED event only if the window has not
+ * previously been notified, i.e. it became visible after the drag operation
+ * was begun. This is a rare case.
+ */
+ private void sendDragStartedIfNeededLw(WindowState newWin) {
+ if (mDragInProgress) {
+ // If we have sent the drag-started, we needn't do so again
+ for (WindowState ws : mNotifiedWindows) {
+ if (ws == newWin) {
+ return;
+ }
+ }
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "need to send DRAG_STARTED to new window " + newWin);
+ }
+ sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
+ }
+ }
+
+ void broadcastDragEndedLw() {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "broadcasting DRAG_ENDED");
+ }
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
+ 0, 0, null, null, null, mDragResult);
+ for (WindowState ws: mNotifiedWindows) {
+ try {
+ ws.mClient.dispatchDragEvent(evt);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to drag-end window " + ws);
+ }
+ }
+ mNotifiedWindows.clear();
+ mDragInProgress = false;
+ evt.recycle();
+ }
+
+ void endDragLw() {
+ mDragState.broadcastDragEndedLw();
+
+ // stop intercepting input
+ mDragState.unregister();
+ mInputMonitor.setUpdateInputWindowsNeededLw();
+ mInputMonitor.updateInputWindowsLw();
+
+ // free our resources and drop all the object references
+ mDragState.reset();
+ mDragState = null;
+
+ if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-drag rotation");
+ boolean changed = setRotationUncheckedLocked(
+ WindowManagerPolicy.USE_LAST_ROTATION, 0, false);
+ if (changed) {
+ sendNewConfiguration();
+ }
+ }
+
+ void notifyMoveLw(float x, float y) {
+ final int myPid = Process.myPid();
+
+ // Move the surface to the given touch
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION notifyMoveLw");
+ Surface.openTransaction();
+ try {
+ mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY));
+ } finally {
+ Surface.closeTransaction();
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION notifyMoveLw");
+ }
+
+ // Tell the affected window
+ WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+ if (touchedWin == null) {
+ if (DEBUG_DRAG) Slog.d(TAG, "No touched win at x=" + x + " y=" + y);
+ return;
+ }
+ if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0) {
+ final IBinder touchedBinder = touchedWin.mClient.asBinder();
+ if (touchedBinder != mLocalWin) {
+ // This drag is pinned only to the originating window, but the drag
+ // point is outside that window. Pretend it's over empty space.
+ touchedWin = null;
+ }
+ }
+ try {
+ // have we dragged over a new window?
+ if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "sending DRAG_EXITED to " + mTargetWindow);
+ }
+ // force DRAG_EXITED_EVENT if appropriate
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED,
+ x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top,
+ null, null, null, false);
+ mTargetWindow.mClient.dispatchDragEvent(evt);
+ if (myPid != mTargetWindow.mSession.mPid) {
+ evt.recycle();
+ }
+ }
+ if (touchedWin != null) {
+ if (false && DEBUG_DRAG) {
+ Slog.d(TAG, "sending DRAG_LOCATION to " + touchedWin);
+ }
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION,
+ x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
+ null, null, null, false);
+ touchedWin.mClient.dispatchDragEvent(evt);
+ if (myPid != touchedWin.mSession.mPid) {
+ evt.recycle();
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "can't send drag notification to windows");
+ }
+ mTargetWindow = touchedWin;
+ }
+
+ // Tell the drop target about the data. Returns 'true' if we can immediately
+ // dispatch the global drag-ended message, 'false' if we need to wait for a
+ // result from the recipient.
+ boolean notifyDropLw(float x, float y) {
+ WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+ if (touchedWin == null) {
+ // "drop" outside a valid window -- no recipient to apply a
+ // timeout to, and we can send the drag-ended message immediately.
+ mDragResult = false;
+ return true;
+ }
+
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "sending DROP to " + touchedWin);
+ }
+ final int myPid = Process.myPid();
+ final IBinder token = touchedWin.mClient.asBinder();
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP,
+ x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
+ null, null, mData, false);
+ try {
+ touchedWin.mClient.dispatchDragEvent(evt);
+
+ // 5 second timeout for this window to respond to the drop
+ mH.removeMessages(H.DRAG_END_TIMEOUT, token);
+ Message msg = mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
+ mH.sendMessageDelayed(msg, 5000);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "can't send drop notification to win " + touchedWin);
+ return true;
+ } finally {
+ if (myPid != touchedWin.mSession.mPid) {
+ evt.recycle();
+ }
+ }
+ mToken = token;
+ return false;
+ }
+
+ // Find the visible, touch-deliverable window under the given point
+ private WindowState getTouchedWinAtPointLw(float xf, float yf) {
+ WindowState touchedWin = null;
+ final int x = (int) xf;
+ final int y = (int) yf;
+ final ArrayList<WindowState> windows = mWindows;
+ final int N = windows.size();
+ for (int i = N - 1; i >= 0; i--) {
+ WindowState child = windows.get(i);
+ final int flags = child.mAttrs.flags;
+ if (!child.isVisibleLw()) {
+ // not visible == don't tell about drags
+ continue;
+ }
+ if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+ // not touchable == don't tell about drags
+ continue;
+ }
+
+ child.getTouchableRegion(mTmpRegion);
+
+ final int touchFlags = flags &
+ (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+ if (mTmpRegion.contains(x, y) || touchFlags == 0) {
+ // Found it
+ touchedWin = child;
+ break;
+ }
+ }
+
+ return touchedWin;
+ }
+ }
+
+ DragState mDragState = null;
+ private final InputHandler mDragInputHandler = new BaseInputHandler() {
+ @Override
+ public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+ boolean handled = false;
+ try {
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0
+ && mDragState != null) {
+ boolean endDrag = false;
+ final float newX = event.getRawX();
+ final float newY = event.getRawY();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ if (DEBUG_DRAG) {
+ Slog.w(TAG, "Unexpected ACTION_DOWN in drag layer");
+ }
+ } break;
+
+ case MotionEvent.ACTION_MOVE: {
+ synchronized (mWindowMap) {
+ // move the surface and tell the involved window(s) where we are
+ mDragState.notifyMoveLw(newX, newY);
+ }
+ } break;
+
+ case MotionEvent.ACTION_UP: {
+ if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at "
+ + newX + "," + newY);
+ synchronized (mWindowMap) {
+ endDrag = mDragState.notifyDropLw(newX, newY);
+ }
+ } break;
+
+ case MotionEvent.ACTION_CANCEL: {
+ if (DEBUG_DRAG) Slog.d(TAG, "Drag cancelled!");
+ endDrag = true;
+ } break;
+ }
+
+ if (endDrag) {
+ if (DEBUG_DRAG) Slog.d(TAG, "Drag ended; tearing down state");
+ // tell all the windows that the drag has ended
+ synchronized (mWindowMap) {
+ mDragState.endDragLw();
+ }
+ }
+
+ handled = true;
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception caught by drag handleMotion", e);
+ } finally {
+ finishedCallback.finished(handled);
+ }
+ }
+ };
+
+ /**
* Whether the UI is currently running in touch mode (not showing
* navigational focus because the user is directly pressing the screen).
*/
@@ -555,6 +954,11 @@ public class WindowManagerService extends IWindowManager.Stub
notifyAll();
}
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode logging for WMThread's Looper");
+ }
+
Looper.loop();
}
}
@@ -592,6 +996,11 @@ public class WindowManagerService extends IWindowManager.Stub
notifyAll();
}
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode for PolicyThread's Looper");
+ }
+
Looper.loop();
}
}
@@ -665,7 +1074,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void placeWindowAfter(WindowState pos, WindowState window) {
final int i = mWindows.indexOf(pos);
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + window + " at "
+ (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
mWindows.add(i+1, window);
@@ -674,7 +1083,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void placeWindowBefore(WindowState pos, WindowState window) {
final int i = mWindows.indexOf(pos);
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + window + " at "
+ i + " of " + mWindows.size() + " (before " + pos + ")");
mWindows.add(i, window);
@@ -732,9 +1141,10 @@ public class WindowManagerService extends IWindowManager.Stub
//apptoken note that the window could be a floating window
//that was created later or a window at the top of the list of
//windows associated with this token.
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
- TAG, "Adding window " + win + " at "
- + (newIdx+1) + " of " + N);
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG, "Adding window " + win + " at "
+ + (newIdx+1) + " of " + N);
+ }
localmWindows.add(newIdx+1, win);
mWindowsChanged = true;
}
@@ -813,9 +1223,10 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
}
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
- TAG, "Adding window " + win + " at "
- + i + " of " + N);
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG, "Adding window " + win + " at "
+ + i + " of " + N);
+ }
localmWindows.add(i, win);
mWindowsChanged = true;
}
@@ -831,13 +1242,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
if (i < 0) i = 0;
- if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Slog.v(
+ if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding window " + win + " at "
+ i + " of " + N);
localmWindows.add(i, win);
mWindowsChanged = true;
}
if (addToToken) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
token.windows.add(tokenWindowsPos, win);
}
@@ -860,6 +1272,7 @@ public class WindowManagerService extends IWindowManager.Stub
// in the same sublayer.
if (wSublayer >= sublayer) {
if (addToToken) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
token.windows.add(i, win);
}
placeWindowBefore(
@@ -871,6 +1284,7 @@ public class WindowManagerService extends IWindowManager.Stub
// in the same sublayer.
if (wSublayer > sublayer) {
if (addToToken) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
token.windows.add(i, win);
}
placeWindowBefore(w, win);
@@ -880,6 +1294,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (i >= NA) {
if (addToToken) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token);
token.windows.add(win);
}
if (sublayer < 0) {
@@ -939,7 +1354,20 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- mUpcomingInputMethodTarget = w;
+ // Now, a special case -- if the last target's window is in the
+ // process of exiting, and is above the new target, keep on the
+ // last target to avoid flicker. Consider for example a Dialog with
+ // the IME shown: when the Dialog is dismissed, we want to keep
+ // the IME above it until it is completely gone so it doesn't drop
+ // behind the dialog or its full-screen scrim.
+ if (mInputMethodTarget != null && w != null
+ && mInputMethodTarget.isDisplayedLw()
+ && mInputMethodTarget.mExiting) {
+ if (mInputMethodTarget.mAnimLayer > w.mAnimLayer) {
+ w = mInputMethodTarget;
+ i = localmWindows.indexOf(w);
+ }
+ }
if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Desired input method target="
+ w + " willMove=" + willMove);
@@ -1041,7 +1469,7 @@ public class WindowManagerService extends IWindowManager.Stub
int pos = findDesiredInputMethodWindowIndexLocked(true);
if (pos >= 0) {
win.mTargetAppToken = mInputMethodTarget.mAppToken;
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(
+ if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
TAG, "Adding input method window " + win + " at " + pos);
mWindows.add(pos, win);
mWindowsChanged = true;
@@ -1228,7 +1656,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
imPos = tmpRemoveWindowLocked(imPos, imWin);
if (DEBUG_INPUT_METHOD) {
- Slog.v(TAG, "List after moving with new pos " + imPos + ":");
+ Slog.v(TAG, "List after removing with new pos " + imPos + ":");
logWindowList(" ");
}
imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
@@ -1305,6 +1733,7 @@ public class WindowManagerService extends IWindowManager.Stub
int foundI = 0;
WindowState topCurW = null;
int topCurI = 0;
+ int windowDetachedI = -1;
int i = N;
while (i > 0) {
i--;
@@ -1317,13 +1746,12 @@ public class WindowManagerService extends IWindowManager.Stub
continue;
}
topCurW = null;
- if (w.mAppToken != null) {
+ if (w != mWindowDetachedWallpaper && w.mAppToken != null) {
// If this window's app token is hidden and not animating,
// it is of no interest to us.
if (w.mAppToken.hidden && w.mAppToken.animation == null) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
- "Skipping hidden or animating token: " + w);
- topCurW = null;
+ "Skipping not hidden or animating token: " + w);
continue;
}
}
@@ -1348,9 +1776,18 @@ public class WindowManagerService extends IWindowManager.Stub
continue;
}
break;
+ } else if (w == mWindowDetachedWallpaper) {
+ windowDetachedI = i;
}
}
+ if (foundW == null && windowDetachedI >= 0) {
+ if (DEBUG_WALLPAPER) Slog.v(TAG,
+ "Found animating detached wallpaper activity: #" + i + "=" + w);
+ foundW = w;
+ foundI = windowDetachedI;
+ }
+
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
// If we are currently waiting for an app transition, and either
// the current target or the next target are involved with it,
@@ -1482,6 +1919,7 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState wb = localmWindows.get(foundI-1);
if (wb.mBaseLayer < maxLayer &&
wb.mAttachedWindow != foundW &&
+ wb.mAttachedWindow != foundW.mAttachedWindow &&
(wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
wb.mToken != foundW.mToken)) {
// This window is not related to the previous one in any
@@ -1581,9 +2019,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
// Now stick it in.
- if (DEBUG_WALLPAPER || DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
- "Moving wallpaper " + wallpaper
- + " from " + oldIndex + " to " + foundI);
+ if (DEBUG_WALLPAPER || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
+ Slog.v(TAG, "Moving wallpaper " + wallpaper
+ + " from " + oldIndex + " to " + foundI);
+ }
localmWindows.add(foundI, wallpaper);
mWindowsChanged = true;
@@ -1788,18 +2227,11 @@ public class WindowManagerService extends IWindowManager.Stub
boolean reportNewConfig = false;
WindowState attachedWindow = null;
WindowState win = null;
+ long origId;
synchronized(mWindowMap) {
- // Instantiating a Display requires talking with the simulator,
- // so don't do it until we know the system is mostly up and
- // running.
if (mDisplay == null) {
- WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- mDisplay = wm.getDefaultDisplay();
- mInitialDisplayWidth = mDisplay.getWidth();
- mInitialDisplayHeight = mDisplay.getHeight();
- mInputManager.setDisplaySize(0, mInitialDisplayWidth, mInitialDisplayHeight);
- reportNewConfig = true;
+ throw new IllegalStateException("Display has not been initialialized");
}
if (mWindowMap.containsKey(client.asBinder())) {
@@ -1898,18 +2330,17 @@ public class WindowManagerService extends IWindowManager.Stub
win.mInputChannel = inputChannels[0];
inputChannels[1].transferToBinderOutParameter(outInputChannel);
- mInputManager.registerInputChannel(win.mInputChannel);
+ mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
// From now on, no exceptions or errors allowed!
res = WindowManagerImpl.ADD_OKAY;
- final long origId = Binder.clearCallingIdentity();
+ origId = Binder.clearCallingIdentity();
if (addToken) {
mTokenMap.put(attrs.token, token);
- mTokenList.add(token);
}
win.attach();
mWindowMap.put(client.asBinder(), win);
@@ -1953,7 +2384,8 @@ public class WindowManagerService extends IWindowManager.Stub
boolean focusChanged = false;
if (win.canReceiveKeys()) {
- focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);
+ focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
+ false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
@@ -1970,26 +2402,23 @@ public class WindowManagerService extends IWindowManager.Stub
//dump();
if (focusChanged) {
- finishUpdateFocusedWindowAfterAssignLayersLocked();
+ finishUpdateFocusedWindowAfterAssignLayersLocked(false /*updateInputWindows*/);
}
-
+ mInputMonitor.updateInputWindowsLw();
+
if (localLOGV) Slog.v(
TAG, "New client " + client.asBinder()
+ ": window=" + win);
- if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked()) {
+ if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
reportNewConfig = true;
}
}
- // sendNewConfiguration() checks caller permissions so we must call it with
- // privilege. updateOrientationFromAppTokens() clears and resets the caller
- // identity anyway, so it's safe to just clear & restore around this whole
- // block.
- final long origId = Binder.clearCallingIdentity();
if (reportNewConfig) {
sendNewConfiguration();
}
+
Binder.restoreCallingIdentity(origId);
return res;
@@ -2052,8 +2481,10 @@ public class WindowManagerService extends IWindowManager.Stub
win.mExiting = true;
win.mRemoveOnExit = true;
mLayoutNeeded = true;
- updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+ updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/);
performLayoutAndPlaceSurfacesLocked();
+ mInputMonitor.updateInputWindowsLw();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
@@ -2068,14 +2499,26 @@ public class WindowManagerService extends IWindowManager.Stub
// So just update orientation if needed.
if (wasVisible && computeForcedAppOrientationLocked()
!= mForcedAppOrientation
- && updateOrientationFromAppTokensLocked()) {
+ && updateOrientationFromAppTokensLocked(false)) {
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
- updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
+ updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
Binder.restoreCallingIdentity(origId);
}
private void removeWindowInnerLocked(Session session, WindowState win) {
+ if (win.mRemoved) {
+ // Nothing to do.
+ return;
+ }
+
+ for (int i=win.mChildWindows.size()-1; i>=0; i--) {
+ WindowState cwin = win.mChildWindows.get(i);
+ Slog.w(TAG, "Force-removing child win " + cwin + " from container "
+ + win);
+ removeWindowInnerLocked(cwin.mSession, cwin);
+ }
+
win.mRemoved = true;
if (mInputMethodTarget == win) {
@@ -2091,8 +2534,10 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.removeWindowLw(win);
win.removeLocked();
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win);
mWindowMap.remove(win.mClient.asBinder());
mWindows.remove(win);
+ mPendingRemove.remove(win);
mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
@@ -2104,6 +2549,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowToken token = win.mToken;
final AppWindowToken atoken = win.mAppToken;
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing " + win + " from " + token);
token.windows.remove(win);
if (atoken != null) {
atoken.allAppWindows.remove(win);
@@ -2114,7 +2560,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (token.windows.size() == 0) {
if (!token.explicit) {
mTokenMap.remove(token.token);
- mTokenList.remove(token);
} else if (atoken != null) {
atoken.firstWindowDrawn = false;
}
@@ -2155,6 +2600,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ mInputMonitor.setUpdateInputWindowsNeededLw();
mInputMonitor.updateInputWindowsLw();
}
@@ -2174,15 +2620,17 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowState w = windowForClientLocked(session, client, false);
if ((w != null) && (w.mSurface != null)) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
+ if (SHOW_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION setTransparentRegion");
Surface.openTransaction();
try {
if (SHOW_TRANSACTIONS) logSurface(w,
"transparentRegionHint=" + region, null);
w.mSurface.setTransparentRegionHint(region);
} finally {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION");
Surface.closeTransaction();
+ if (SHOW_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION setTransparentRegion");
}
}
}
@@ -2193,7 +2641,7 @@ public class WindowManagerService extends IWindowManager.Stub
void setInsetsWindow(Session session, IWindow client,
int touchableInsets, Rect contentInsets,
- Rect visibleInsets) {
+ Rect visibleInsets, Region touchableRegion) {
long origId = Binder.clearCallingIdentity();
try {
synchronized (mWindowMap) {
@@ -2202,6 +2650,7 @@ public class WindowManagerService extends IWindowManager.Stub
w.mGivenInsetsPending = false;
w.mGivenContentInsets.set(contentInsets);
w.mGivenVisibleInsets.set(visibleInsets);
+ w.mGivenTouchableRegion.set(touchableRegion);
w.mTouchableInsets = touchableInsets;
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
@@ -2346,7 +2795,10 @@ public class WindowManagerService extends IWindowManager.Stub
displayed = !win.isVisibleLw();
if (win.mExiting) {
win.mExiting = false;
- win.mAnimation = null;
+ if (win.mAnimation != null) {
+ win.mAnimation.cancel();
+ win.mAnimation = null;
+ }
}
if (win.mDestroying) {
win.mDestroying = false;
@@ -2399,6 +2851,7 @@ public class WindowManagerService extends IWindowManager.Stub
outSurface.release();
}
} catch (Exception e) {
+ mInputMonitor.setUpdateInputWindowsNeededLw();
mInputMonitor.updateInputWindowsLw();
Slog.w(TAG, "Exception thrown when creating surface for client "
@@ -2486,7 +2939,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (focusMayChange) {
//System.out.println("Focus may change: " + win.mAttrs.getTitle());
- if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
+ if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/)) {
imMayMove = false;
}
//System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
@@ -2517,7 +2971,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (assignLayers) {
assignLayersLocked();
}
- configChanged = updateOrientationFromAppTokensLocked();
+ configChanged = updateOrientationFromAppTokensLocked(false);
performLayoutAndPlaceSurfacesLocked();
if (displayed && win.mIsWallpaper) {
updateWallpaperOffsetLocked(win, mDisplay.getWidth(),
@@ -2542,6 +2996,7 @@ public class WindowManagerService extends IWindowManager.Stub
inTouchMode = mInTouchMode;
+ mInputMonitor.setUpdateInputWindowsNeededLw();
mInputMonitor.updateInputWindowsLw();
}
@@ -2571,7 +3026,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
- if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: params package="
+ if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
+ (lp != null ? lp.packageName : null)
+ " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
if (lp != null && lp.windowAnimations != 0) {
@@ -2592,7 +3047,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
- if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: params package="
+ if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package="
+ packageName + " resId=0x" + Integer.toHexString(resId));
if (packageName != null) {
if ((resId&0xFF000000) == 0x01000000) {
@@ -2874,7 +3329,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
wtoken = new WindowToken(token, type, true);
mTokenMap.put(token, wtoken);
- mTokenList.add(wtoken);
if (type == TYPE_WALLPAPER) {
mWallpaperTokens.add(wtoken);
}
@@ -2890,7 +3344,6 @@ public class WindowManagerService extends IWindowManager.Stub
final long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
WindowToken wtoken = mTokenMap.remove(token);
- mTokenList.remove(wtoken);
if (wtoken != null) {
boolean delayed = false;
if (!wtoken.hidden) {
@@ -2916,7 +3369,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (changed) {
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
- updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
+ updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ false /*updateInputWindows*/);
}
if (delayed) {
@@ -2926,6 +3380,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ mInputMonitor.setUpdateInputWindowsNeededLw();
mInputMonitor.updateInputWindowsLw();
} else {
Slog.w(TAG, "Attempted to remove non-existing token: " + token);
@@ -2966,10 +3421,9 @@ public class WindowManagerService extends IWindowManager.Stub
wtoken.groupId = groupId;
wtoken.appFullscreen = fullscreen;
wtoken.requestedOrientation = requestedOrientation;
+ if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + wtoken);
mAppTokens.add(addPos, wtoken);
- if (localLOGV) Slog.v(TAG, "Adding new app token: " + wtoken);
mTokenMap.put(token.asBinder(), wtoken);
- mTokenList.add(wtoken);
// Application tokens start out hidden.
wtoken.hidden = true;
@@ -3086,7 +3540,7 @@ public class WindowManagerService extends IWindowManager.Stub
long ident = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
- if (updateOrientationFromAppTokensLocked()) {
+ if (updateOrientationFromAppTokensLocked(false)) {
if (freezeThisOneIfNeeded != null) {
AppWindowToken wtoken = findAppWindowToken(
freezeThisOneIfNeeded);
@@ -3108,7 +3562,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
mLayoutNeeded = true;
- startFreezingDisplayLocked();
+ startFreezingDisplayLocked(false);
config = new Configuration(mTempConfiguration);
}
}
@@ -3133,8 +3587,8 @@ public class WindowManagerService extends IWindowManager.Stub
* @see android.view.IWindowManager#updateOrientationFromAppTokens(
* android.os.IBinder)
*/
- boolean updateOrientationFromAppTokensLocked() {
- if (mDisplayFrozen) {
+ boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
+ if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
// If the display is frozen, some activities may be in the middle
// of restarting, and thus have removed their old window. If the
// window has the flag to hide the lock screen, then the lock screen
@@ -3154,7 +3608,8 @@ public class WindowManagerService extends IWindowManager.Stub
//action like disabling/enabling sensors etc.,
mPolicy.setCurrentOrientationLw(req);
if (setRotationUncheckedLocked(WindowManagerPolicy.USE_LAST_ROTATION,
- mLastRotationFlags | Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE)) {
+ mLastRotationFlags | Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE,
+ inTransaction)) {
changed = true;
}
}
@@ -3245,13 +3700,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (moveFocusNow && changed) {
final long origId = Binder.clearCallingIdentity();
- updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
+ updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
Binder.restoreCallingIdentity(origId);
}
}
}
- public void prepareAppTransition(int transit) {
+ public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"prepareAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3265,14 +3720,16 @@ public class WindowManagerService extends IWindowManager.Stub
if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET
|| mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
mNextAppTransition = transit;
- } else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
- && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) {
- // Opening a new task always supersedes a close for the anim.
- mNextAppTransition = transit;
- } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
- && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) {
- // Opening a new activity always supersedes a close for the anim.
- mNextAppTransition = transit;
+ } else if (!alwaysKeepCurrent) {
+ if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
+ && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) {
+ // Opening a new task always supersedes a close for the anim.
+ mNextAppTransition = transit;
+ } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
+ && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) {
+ // Opening a new activity always supersedes a close for the anim.
+ mNextAppTransition = transit;
+ }
}
mAppTransitionReady = false;
mAppTransitionTimeout = false;
@@ -3322,7 +3779,7 @@ public class WindowManagerService extends IWindowManager.Stub
public void setAppStartingWindow(IBinder token, String pkg,
int theme, CharSequence nonLocalizedLabel, int labelRes, int icon,
- IBinder transferFrom, boolean createIfNeeded) {
+ int windowFlags, IBinder transferFrom, boolean createIfNeeded) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppStartingIcon()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3378,10 +3835,12 @@ public class WindowManagerService extends IWindowManager.Stub
startingWindow.mToken = wtoken;
startingWindow.mRootToken = wtoken;
startingWindow.mAppToken = wtoken;
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
+ if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
"Removing starting window: " + startingWindow);
mWindows.remove(startingWindow);
mWindowsChanged = true;
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing starting " + startingWindow
+ + " from " + ttoken);
ttoken.windows.remove(startingWindow);
ttoken.allAppWindows.remove(startingWindow);
addWindowToListInOrderLocked(startingWindow, true);
@@ -3416,7 +3875,8 @@ public class WindowManagerService extends IWindowManager.Stub
ttoken.updateLayers();
}
- updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+ updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ true /*updateInputWindows*/);
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
Binder.restoreCallingIdentity(origId);
@@ -3470,7 +3930,7 @@ public class WindowManagerService extends IWindowManager.Stub
mStartingIconInTransition = true;
wtoken.startingData = new StartingData(
pkg, theme, nonLocalizedLabel,
- labelRes, icon);
+ labelRes, icon, windowFlags);
Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
// Note: we really want to do sendMessageAtFrontOfQueue() because we
// want to process the message ASAP, before any other queued
@@ -3576,12 +4036,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (changed) {
mLayoutNeeded = true;
+ mInputMonitor.setUpdateInputWindowsNeededLw();
if (performLayout) {
- updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+ updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/);
performLayoutAndPlaceSurfacesLocked();
- } else {
- mInputMonitor.updateInputWindowsLw();
}
+ mInputMonitor.updateInputWindowsLw();
}
}
@@ -3727,7 +4188,7 @@ public class WindowManagerService extends IWindowManager.Stub
wtoken.freezingScreen = true;
mAppsFreezingScreen++;
if (mAppsFreezingScreen == 1) {
- startFreezingDisplayLocked();
+ startFreezingDisplayLocked(false);
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mH.sendMessageDelayed(mH.obtainMessage(H.APP_FREEZE_TIMEOUT),
5000);
@@ -3796,7 +4257,6 @@ public class WindowManagerService extends IWindowManager.Stub
final long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
WindowToken basewtoken = mTokenMap.remove(token);
- mTokenList.remove(basewtoken);
if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken);
delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_UNSET, true);
@@ -3816,6 +4276,8 @@ public class WindowManagerService extends IWindowManager.Stub
+ " animating=" + wtoken.animating);
if (delayed) {
// set the token aside because it has an active animation to be finished
+ if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
+ "removeAppToken make exiting: " + wtoken);
mExitingAppTokens.add(wtoken);
} else {
// Make sure there is no animation running on this token,
@@ -3824,11 +4286,9 @@ public class WindowManagerService extends IWindowManager.Stub
wtoken.animation = null;
wtoken.animating = false;
}
+ if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
+ "removeAppToken: " + wtoken);
mAppTokens.remove(wtoken);
- if (mLastEnterAnimToken == wtoken) {
- mLastEnterAnimToken = null;
- mLastEnterAnimParams = null;
- }
wtoken.removed = true;
if (wtoken.startingData != null) {
startingToken = wtoken;
@@ -3837,7 +4297,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mFocusedApp == wtoken) {
if (DEBUG_FOCUS) Slog.v(TAG, "Removing focused app token:" + wtoken);
mFocusedApp = null;
- updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
+ updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
mInputMonitor.setFocusedAppLw(null);
}
} else {
@@ -3954,18 +4414,21 @@ public class WindowManagerService extends IWindowManager.Stub
if (!added && cwin.mSubLayer >= 0) {
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding child window at "
+ index + ": " + cwin);
+ win.mRebuilding = false;
mWindows.add(index, win);
index++;
added = true;
}
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at "
+ index + ": " + cwin);
+ cwin.mRebuilding = false;
mWindows.add(index, cwin);
index++;
}
if (!added) {
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Re-adding window at "
+ index + ": " + win);
+ win.mRebuilding = false;
mWindows.add(index, win);
index++;
}
@@ -3991,6 +4454,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_REORDER) Slog.v(TAG, "Initial app tokens:");
if (DEBUG_REORDER) dumpAppTokensLocked();
final AppWindowToken wtoken = findAppWindowToken(token);
+ if (DEBUG_TOKEN_MOVEMENT || DEBUG_REORDER) Slog.v(TAG,
+ "Start moving token " + wtoken + " initially at "
+ + mAppTokens.indexOf(wtoken));
if (wtoken == null || !mAppTokens.remove(wtoken)) {
Slog.w(TAG, "Attempting to reorder token that doesn't exist: "
+ token + " (" + wtoken + ")");
@@ -3998,6 +4464,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
mAppTokens.add(index, wtoken);
if (DEBUG_REORDER) Slog.v(TAG, "Moved " + token + " to " + index + ":");
+ else if (DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "Moved " + token + " to " + index);
if (DEBUG_REORDER) dumpAppTokensLocked();
final long origId = Binder.clearCallingIdentity();
@@ -4009,9 +4476,11 @@ public class WindowManagerService extends IWindowManager.Stub
reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken);
if (DEBUG_REORDER) Slog.v(TAG, "Final window list:");
if (DEBUG_REORDER) dumpWindowsLocked();
- updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+ updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/);
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
+ mInputMonitor.updateInputWindowsLw();
}
Binder.restoreCallingIdentity(origId);
}
@@ -4025,6 +4494,8 @@ public class WindowManagerService extends IWindowManager.Stub
for (int i=0; i<N; i++) {
IBinder token = tokens.get(i);
final AppWindowToken wtoken = findAppWindowToken(token);
+ if (DEBUG_REORDER || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
+ "Temporarily removing " + wtoken + " from " + mAppTokens.indexOf(wtoken));
if (!mAppTokens.remove(wtoken)) {
Slog.w(TAG, "Attempting to reorder token that doesn't exist: "
+ token + " (" + wtoken + ")");
@@ -4046,11 +4517,13 @@ public class WindowManagerService extends IWindowManager.Stub
pos = reAddAppWindowsLocked(pos, wtoken);
if (updateFocusAndLayout) {
- if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/)) {
assignLayersLocked();
}
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
+ mInputMonitor.updateInputWindowsLw();
}
}
@@ -4076,11 +4549,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/)) {
assignLayersLocked();
}
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
+ mInputMonitor.updateInputWindowsLw();
//dump();
}
@@ -4098,6 +4573,8 @@ public class WindowManagerService extends IWindowManager.Stub
for (int i=0; i<N; i++) {
AppWindowToken wt = findAppWindowToken(tokens.get(i));
if (wt != null) {
+ if (DEBUG_TOKEN_MOVEMENT || DEBUG_REORDER) Slog.v(TAG,
+ "Adding next to top: " + wt);
mAppTokens.add(wt);
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mToTopApps.remove(wt);
@@ -4130,6 +4607,8 @@ public class WindowManagerService extends IWindowManager.Stub
for (int i=0; i<N; i++) {
AppWindowToken wt = findAppWindowToken(tokens.get(i));
if (wt != null) {
+ if (DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
+ "Adding next to bottom: " + wt + " at " + pos);
mAppTokens.add(pos, wt);
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mToTopApps.remove(wt);
@@ -4435,8 +4914,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = mWindows.size();
for (int i=0; i<N; i++) {
WindowState w = mWindows.get(i);
- if (w.isVisibleLw() && !w.mObscured
- && (w.mOrientationChanging || !w.isDrawnLw())) {
+ if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
return;
}
}
@@ -4477,6 +4955,164 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ // TODO: more accounting of which pid(s) turned it on, keep count,
+ // only allow disables from pids which have count on, etc.
+ public void showStrictModeViolation(boolean on) {
+ int pid = Binder.getCallingPid();
+ synchronized(mWindowMap) {
+ // Ignoring requests to enable the red border from clients
+ // which aren't on screen. (e.g. Broadcast Receivers in
+ // the background..)
+ if (on) {
+ boolean isVisible = false;
+ for (WindowState ws : mWindows) {
+ if (ws.mSession.mPid == pid && ws.isVisibleLw()) {
+ isVisible = true;
+ break;
+ }
+ }
+ if (!isVisible) {
+ return;
+ }
+ }
+
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION showStrictModeViolation");
+ Surface.openTransaction();
+ try {
+ if (mStrictModeFlash == null) {
+ mStrictModeFlash = new StrictModeFlash(mDisplay, mFxSession);
+ }
+ mStrictModeFlash.setVisibility(on);
+ } finally {
+ Surface.closeTransaction();
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION showStrictModeViolation");
+ }
+ }
+ }
+
+ public void setStrictModeVisualIndicatorPreference(String value) {
+ SystemProperties.set(StrictMode.VISUAL_PROPERTY, value);
+ }
+
+ public Bitmap screenshotApplications(IBinder appToken, int maxWidth, int maxHeight) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
+ "screenshotApplications()")) {
+ throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+ }
+
+ Bitmap rawss;
+
+ int maxLayer = 0;
+ boolean foundApp;
+ final Rect frame = new Rect();
+
+ float scale;
+ int sw, sh, dw, dh;
+ int rot;
+
+ synchronized(mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+
+ int aboveAppLayer = mPolicy.windowTypeToLayerLw(
+ WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
+ aboveAppLayer += TYPE_LAYER_MULTIPLIER;
+
+ // Figure out the part of the screen that is actually the app.
+ for (int i=0; i<mWindows.size(); i++) {
+ WindowState ws = mWindows.get(i);
+ if (ws.mSurface == null) {
+ continue;
+ }
+ if (ws.mLayer >= aboveAppLayer) {
+ break;
+ }
+ if (appToken != null && (ws.mAppToken == null
+ || ws.mAppToken.token != appToken)) {
+ continue;
+ }
+ if (maxLayer < ws.mAnimLayer) {
+ maxLayer = ws.mAnimLayer;
+ }
+ final Rect wf = ws.mFrame;
+ final Rect cr = ws.mContentInsets;
+ int left = wf.left + cr.left;
+ int top = wf.top + cr.top;
+ int right = wf.right - cr.right;
+ int bottom = wf.bottom - cr.bottom;
+ frame.union(left, top, right, bottom);
+ }
+ Binder.restoreCallingIdentity(ident);
+
+ if (frame.isEmpty() || maxLayer == 0) {
+ return null;
+ }
+
+ // The screenshot API does not apply the current screen rotation.
+ rot = mDisplay.getRotation();
+ int fw = frame.width();
+ int fh = frame.height();
+
+ // First try reducing to fit in x dimension.
+ scale = maxWidth/(float)fw;
+ sw = maxWidth;
+ sh = (int)(fh*scale);
+ if (sh > maxHeight) {
+ // y dimension became too long; constrain by that.
+ scale = maxHeight/(float)fh;
+ sw = (int)(fw*scale);
+ sh = maxHeight;
+ }
+
+ // The screen shot will contain the entire screen.
+ dw = (int)(mDisplay.getWidth()*scale);
+ dh = (int)(mDisplay.getHeight()*scale);
+ if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+ int tmp = dw;
+ dw = dh;
+ dh = tmp;
+ rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
+ }
+ rawss = Surface.screenshot(dw, dh, 0, maxLayer);
+ }
+
+ if (rawss == null) {
+ Log.w(TAG, "Failure taking screenshot for (" + dw + "x" + dh
+ + ") to layer " + maxLayer);
+ return null;
+ }
+
+ Bitmap bm = Bitmap.createBitmap(sw, sh, rawss.getConfig());
+ Matrix matrix = new Matrix();
+ ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix);
+ matrix.postTranslate(-(int)(frame.left*scale), -(int)(frame.top*scale));
+ Canvas canvas = new Canvas(bm);
+ canvas.drawBitmap(rawss, matrix, null);
+
+ rawss.recycle();
+ return bm;
+ }
+
+ public void freezeRotation() {
+ if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
+ "setRotation()")) {
+ throw new SecurityException("Requires SET_ORIENTATION permission");
+ }
+
+ mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED, mRotation);
+ setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false, 0);
+ }
+
+ public void thawRotation() {
+ if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
+ "setRotation()")) {
+ throw new SecurityException("Requires SET_ORIENTATION permission");
+ }
+
+ mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE, 0);
+ setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false, 0);
+ }
+
public void setRotation(int rotation,
boolean alwaysSendConfiguration, int animFlags) {
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
@@ -4495,7 +5131,7 @@ public class WindowManagerService extends IWindowManager.Stub
long origId = Binder.clearCallingIdentity();
boolean changed;
synchronized(mWindowMap) {
- changed = setRotationUncheckedLocked(rotation, animFlags);
+ changed = setRotationUncheckedLocked(rotation, animFlags, false);
}
if (changed || alwaysSendConfiguration) {
@@ -4513,14 +5149,31 @@ public class WindowManagerService extends IWindowManager.Stub
* Returns null if the rotation has been changed. In this case YOU
* MUST CALL setNewConfiguration() TO UNFREEZE THE SCREEN.
*/
- public boolean setRotationUncheckedLocked(int rotation, int animFlags) {
+ public boolean setRotationUncheckedLocked(int rotation, int animFlags, boolean inTransaction) {
+ if (mDragState != null || mScreenRotationAnimation != null) {
+ // Potential rotation during a drag. Don't do the rotation now, but make
+ // a note to perform the rotation later.
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation.");
+ if (rotation != WindowManagerPolicy.USE_LAST_ROTATION) {
+ mDeferredRotation = rotation;
+ mDeferredRotationAnimFlags = animFlags;
+ }
+ return false;
+ }
+
boolean changed;
if (rotation == WindowManagerPolicy.USE_LAST_ROTATION) {
+ if (mDeferredRotation != WindowManagerPolicy.USE_LAST_ROTATION) {
+ rotation = mDeferredRotation;
+ mRequestedRotation = rotation;
+ mLastRotationFlags = mDeferredRotationAnimFlags;
+ }
rotation = mRequestedRotation;
} else {
mRequestedRotation = rotation;
mLastRotationFlags = animFlags;
}
+ mDeferredRotation = WindowManagerPolicy.USE_LAST_ROTATION;
if (DEBUG_ORIENTATION) Slog.v(TAG, "Overwriting rotation value from " + rotation);
rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation,
mRotation, mDisplayEnabled);
@@ -4540,11 +5193,33 @@ public class WindowManagerService extends IWindowManager.Stub
2000);
mWaitingForConfig = true;
mLayoutNeeded = true;
- startFreezingDisplayLocked();
+ startFreezingDisplayLocked(inTransaction);
Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags);
mInputManager.setDisplayOrientation(0, rotation);
if (mDisplayEnabled) {
- Surface.setOrientation(0, rotation, animFlags);
+ if (CUSTOM_SCREEN_ROTATION) {
+ Surface.freezeDisplay(0);
+ if (!inTransaction) {
+ if (SHOW_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION setRotationUnchecked");
+ Surface.openTransaction();
+ }
+ try {
+ if (mScreenRotationAnimation != null) {
+ mScreenRotationAnimation.setRotation(rotation);
+ }
+ } finally {
+ if (!inTransaction) {
+ Surface.closeTransaction();
+ if (SHOW_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION setRotationUnchecked");
+ }
+ }
+ Surface.setOrientation(0, rotation, animFlags);
+ Surface.unfreezeDisplay(0);
+ } else {
+ Surface.setOrientation(0, rotation, animFlags);
+ }
}
for (int i=mWindows.size()-1; i>=0; i--) {
WindowState w = mWindows.get(i);
@@ -4958,7 +5633,13 @@ public class WindowManagerService extends IWindowManager.Stub
public Configuration computeNewConfiguration() {
synchronized (mWindowMap) {
- return computeNewConfigurationLocked();
+ Configuration config = computeNewConfigurationLocked();
+ if (config == null && mWaitingForConfig) {
+ // Nothing changed but we are waiting for something... stop that!
+ mWaitingForConfig = false;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ return config;
}
}
@@ -5054,7 +5735,59 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.adjustConfigurationLw(config);
return true;
}
-
+
+ // -------------------------------------------------------------
+ // Drag and drop
+ // -------------------------------------------------------------
+
+ IBinder prepareDragSurface(IWindow window, SurfaceSession session,
+ int flags, int width, int height, Surface outSurface) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "prepare drag surface: w=" + width + " h=" + height
+ + " flags=" + Integer.toHexString(flags) + " win=" + window
+ + " asbinder=" + window.asBinder());
+ }
+
+ final int callerPid = Binder.getCallingPid();
+ final long origId = Binder.clearCallingIdentity();
+ IBinder token = null;
+
+ try {
+ synchronized (mWindowMap) {
+ try {
+ if (mDragState == null) {
+ Surface surface = new Surface(session, callerPid, "drag surface", 0,
+ width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+ outSurface.copyFrom(surface);
+ final IBinder winBinder = window.asBinder();
+ token = new Binder();
+ // TODO: preserve flags param in DragState
+ mDragState = new DragState(token, surface, 0, winBinder);
+ mDragState.mSurface = surface;
+ token = mDragState.mToken = new Binder();
+
+ // 5 second timeout for this window to actually begin the drag
+ mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder);
+ Message msg = mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder);
+ mH.sendMessageDelayed(msg, 5000);
+ } else {
+ Slog.w(TAG, "Drag already in progress");
+ }
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.e(TAG, "Can't allocate drag surface w=" + width + " h=" + height, e);
+ if (mDragState != null) {
+ mDragState.reset();
+ mDragState = null;
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return token;
+ }
+
// -------------------------------------------------------------
// Input Events and Focus Management
// -------------------------------------------------------------
@@ -5074,23 +5807,31 @@ public class WindowManagerService extends IWindowManager.Stub
// When true, input dispatch proceeds normally. Otherwise all events are dropped.
private boolean mInputDispatchEnabled = true;
+ // When true, need to call updateInputWindowsLw().
+ private boolean mUpdateInputWindowsNeeded = true;
+
// Temporary list of windows information to provide to the input dispatcher.
private InputWindowList mTempInputWindows = new InputWindowList();
// Temporary input application object to provide to the input dispatcher.
private InputApplication mTempInputApplication = new InputApplication();
+ // Set to true when the first input device configuration change notification
+ // is received to indicate that the input devices are ready.
+ private final Object mInputDevicesReadyMonitor = new Object();
+ private boolean mInputDevicesReady;
+
/* Notifies the window manager about a broken input channel.
*
* Called by the InputManager.
*/
- public void notifyInputChannelBroken(InputChannel inputChannel) {
+ public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
+ if (inputWindowHandle == null) {
+ return;
+ }
+
synchronized (mWindowMap) {
- WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
- if (windowState == null) {
- return; // irrelevant
- }
-
+ WindowState windowState = (WindowState) inputWindowHandle.windowState;
Slog.i(TAG, "WINDOW DIED " + windowState);
removeWindowLocked(windowState.mSession, windowState);
}
@@ -5101,11 +5842,12 @@ public class WindowManagerService extends IWindowManager.Stub
*
* Called by the InputManager.
*/
- public long notifyANR(Object token, InputChannel inputChannel) {
+ public long notifyANR(InputApplicationHandle inputApplicationHandle,
+ InputWindowHandle inputWindowHandle) {
AppWindowToken appWindowToken = null;
- if (inputChannel != null) {
+ if (inputWindowHandle != null) {
synchronized (mWindowMap) {
- WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ WindowState windowState = (WindowState) inputWindowHandle.windowState;
if (windowState != null) {
Slog.i(TAG, "Input event dispatching timed out sending to "
+ windowState.mAttrs.getTitle());
@@ -5114,8 +5856,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- if (appWindowToken == null && token != null) {
- appWindowToken = (AppWindowToken) token;
+ if (appWindowToken == null && inputApplicationHandle != null) {
+ appWindowToken = inputApplicationHandle.appWindowToken;
Slog.i(TAG, "Input event dispatching timed out sending to application "
+ appWindowToken.stringName);
}
@@ -5135,33 +5877,60 @@ public class WindowManagerService extends IWindowManager.Stub
}
return 0; // abort dispatching
}
-
- private WindowState getWindowStateForInputChannel(InputChannel inputChannel) {
- synchronized (mWindowMap) {
- return getWindowStateForInputChannelLocked(inputChannel);
- }
+
+ private void addDragInputWindowLw(InputWindowList windowList) {
+ final InputWindow inputWindow = windowList.add();
+ inputWindow.inputChannel = mDragState.mServerChannel;
+ inputWindow.name = "drag";
+ inputWindow.layoutParamsFlags = 0;
+ inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
+ inputWindow.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ inputWindow.visible = true;
+ inputWindow.canReceiveKeys = false;
+ inputWindow.hasFocus = true;
+ inputWindow.hasWallpaper = false;
+ inputWindow.paused = false;
+ inputWindow.layer = mDragState.getDragLayerLw();
+ inputWindow.ownerPid = Process.myPid();
+ inputWindow.ownerUid = Process.myUid();
+
+ // The drag window covers the entire display
+ inputWindow.frameLeft = 0;
+ inputWindow.frameTop = 0;
+ inputWindow.frameRight = mDisplay.getWidth();
+ inputWindow.frameBottom = mDisplay.getHeight();
+
+ // The drag window cannot receive new touches.
+ inputWindow.touchableRegion.setEmpty();
}
-
- private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
- int windowCount = mWindows.size();
- for (int i = 0; i < windowCount; i++) {
- WindowState windowState = mWindows.get(i);
- if (windowState.mInputChannel == inputChannel) {
- return windowState;
- }
- }
-
- return null;
+
+ public void setUpdateInputWindowsNeededLw() {
+ mUpdateInputWindowsNeeded = true;
}
-
+
/* Updates the cached window information provided to the input dispatcher. */
public void updateInputWindowsLw() {
+ if (!mUpdateInputWindowsNeeded) {
+ return;
+ }
+ mUpdateInputWindowsNeeded = false;
+
// Populate the input window list with information about all of the windows that
// could potentially receive input.
// As an optimization, we could try to prune the list of windows but this turns
// out to be difficult because only the native code knows for sure which window
// currently has touch focus.
final ArrayList<WindowState> windows = mWindows;
+
+ // If there's a drag in flight, provide a pseudowindow to catch drag input
+ final boolean inDrag = (mDragState != null);
+ if (inDrag) {
+ if (DEBUG_DRAG) {
+ Log.d(TAG, "Inserting drag window");
+ }
+ addDragInputWindowLw(mTempInputWindows);
+ }
+
final int N = windows.size();
for (int i = N - 1; i >= 0; i--) {
final WindowState child = windows.get(i);
@@ -5177,9 +5946,16 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean isVisible = child.isVisibleLw();
final boolean hasWallpaper = (child == mWallpaperTarget)
&& (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
-
+
+ // If there's a drag in progress and 'child' is a potential drop target,
+ // make sure it's been told about the drag
+ if (inDrag && isVisible) {
+ mDragState.sendDragStartedIfNeededLw(child);
+ }
+
// Add a window to our list of input windows.
final InputWindow inputWindow = mTempInputWindows.add();
+ inputWindow.inputWindowHandle = child.mInputWindowHandle;
inputWindow.inputChannel = child.mInputChannel;
inputWindow.name = child.toString();
inputWindow.layoutParamsFlags = flags;
@@ -5199,40 +5975,8 @@ public class WindowManagerService extends IWindowManager.Stub
inputWindow.frameTop = frame.top;
inputWindow.frameRight = frame.right;
inputWindow.frameBottom = frame.bottom;
-
- final Rect visibleFrame = child.mVisibleFrame;
- inputWindow.visibleFrameLeft = visibleFrame.left;
- inputWindow.visibleFrameTop = visibleFrame.top;
- inputWindow.visibleFrameRight = visibleFrame.right;
- inputWindow.visibleFrameBottom = visibleFrame.bottom;
-
- switch (child.mTouchableInsets) {
- default:
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
- inputWindow.touchableAreaLeft = frame.left;
- inputWindow.touchableAreaTop = frame.top;
- inputWindow.touchableAreaRight = frame.right;
- inputWindow.touchableAreaBottom = frame.bottom;
- break;
-
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
- Rect inset = child.mGivenContentInsets;
- inputWindow.touchableAreaLeft = frame.left + inset.left;
- inputWindow.touchableAreaTop = frame.top + inset.top;
- inputWindow.touchableAreaRight = frame.right - inset.right;
- inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
- break;
- }
-
- case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
- Rect inset = child.mGivenVisibleInsets;
- inputWindow.touchableAreaLeft = frame.left + inset.left;
- inputWindow.touchableAreaTop = frame.top + inset.top;
- inputWindow.touchableAreaRight = frame.right - inset.right;
- inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
- break;
- }
- }
+
+ child.getTouchableRegion(inputWindow.touchableRegion);
}
// Send windows to native code.
@@ -5242,7 +5986,32 @@ public class WindowManagerService extends IWindowManager.Stub
// Also avoids keeping InputChannel objects referenced unnecessarily.
mTempInputWindows.clear();
}
-
+
+ /* Notifies that the input device configuration has changed. */
+ public void notifyConfigurationChanged() {
+ sendNewConfiguration();
+
+ synchronized (mInputDevicesReadyMonitor) {
+ if (!mInputDevicesReady) {
+ mInputDevicesReady = true;
+ mInputDevicesReadyMonitor.notifyAll();
+ }
+ }
+ }
+
+ /* Waits until the built-in input devices have been configured. */
+ public boolean waitForInputDevicesReady(long timeoutMillis) {
+ synchronized (mInputDevicesReadyMonitor) {
+ if (!mInputDevicesReady) {
+ try {
+ mInputDevicesReadyMonitor.wait(timeoutMillis);
+ } catch (InterruptedException ex) {
+ }
+ }
+ return mInputDevicesReady;
+ }
+ }
+
/* Notifies that the lid switch changed state. */
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
@@ -5250,26 +6019,31 @@ public class WindowManagerService extends IWindowManager.Stub
/* Provides an opportunity for the window manager policy to intercept early key
* processing as soon as the key has been read from the device. */
- public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
- int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
- return mPolicy.interceptKeyBeforeQueueing(whenNanos, action, flags,
- keyCode, scanCode, policyFlags, isScreenOn);
+ public int interceptKeyBeforeQueueing(
+ KeyEvent event, int policyFlags, boolean isScreenOn) {
+ return mPolicy.interceptKeyBeforeQueueing(event, policyFlags, isScreenOn);
}
/* Provides an opportunity for the window manager policy to process a key before
* ordinary dispatch. */
- public boolean interceptKeyBeforeDispatching(InputChannel focus,
- int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount,
- int policyFlags) {
- WindowState windowState = getWindowStateForInputChannel(focus);
- return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags,
- keyCode, scanCode, metaState, repeatCount, policyFlags);
+ public boolean interceptKeyBeforeDispatching(
+ InputWindowHandle focus, KeyEvent event, int policyFlags) {
+ WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
+ return mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
+ }
+
+ /* Provides an opportunity for the window manager policy to process a key that
+ * the application did not handle. */
+ public KeyEvent dispatchUnhandledKey(
+ InputWindowHandle focus, KeyEvent event, int policyFlags) {
+ WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
+ return mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
}
/* Called when the current input focus changes.
* Layer assignment is assumed to be complete by the time this is called.
*/
- public void setInputFocusLw(WindowState newWindow) {
+ public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
if (DEBUG_INPUT) {
Slog.d(TAG, "Input focus has changed to " + newWindow);
}
@@ -5281,9 +6055,13 @@ public class WindowManagerService extends IWindowManager.Stub
// forgets to resume.
newWindow.mToken.paused = false;
}
-
+
mInputFocus = newWindow;
- updateInputWindowsLw();
+ setUpdateInputWindowsNeededLw();
+
+ if (updateInputWindows) {
+ updateInputWindowsLw();
+ }
}
}
@@ -5292,12 +6070,14 @@ public class WindowManagerService extends IWindowManager.Stub
if (newApp == null) {
mInputManager.setFocusedApplication(null);
} else {
+ mTempInputApplication.inputApplicationHandle = newApp.mInputApplicationHandle;
mTempInputApplication.name = newApp.toString();
mTempInputApplication.dispatchingTimeoutNanos =
newApp.inputDispatchingTimeoutNanos;
- mTempInputApplication.token = newApp;
-
+
mInputManager.setFocusedApplication(mTempInputApplication);
+
+ mTempInputApplication.recycle();
}
}
@@ -5308,6 +6088,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
window.paused = true;
+ setUpdateInputWindowsNeededLw();
updateInputWindowsLw();
}
}
@@ -5319,6 +6100,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
window.paused = false;
+ setUpdateInputWindowsNeededLw();
updateInputWindowsLw();
}
}
@@ -5421,6 +6203,7 @@ public class WindowManagerService extends IWindowManager.Stub
int deviceId = ev.getDeviceId();
int scancode = ev.getScanCode();
int source = ev.getSource();
+ int flags = ev.getFlags();
if (source == InputDevice.SOURCE_UNKNOWN) {
source = InputDevice.SOURCE_KEYBOARD;
@@ -5430,7 +6213,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (downTime == 0) downTime = eventTime;
KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
- deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM, source);
+ deviceId, scancode, flags | KeyEvent.FLAG_FROM_SYSTEM, source);
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -5556,11 +6339,35 @@ public class WindowManagerService extends IWindowManager.Stub
}
public boolean detectSafeMode() {
+ if (!mInputMonitor.waitForInputDevicesReady(
+ INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
+ Slog.w(TAG, "Devices still not ready after waiting "
+ + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS
+ + " milliseconds before attempting to detect safe mode.");
+ }
+
mSafeMode = mPolicy.detectSafeMode();
return mSafeMode;
}
public void systemReady() {
+ synchronized(mWindowMap) {
+ if (mDisplay != null) {
+ throw new IllegalStateException("Display already initialized");
+ }
+ WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = wm.getDefaultDisplay();
+ mInitialDisplayWidth = mDisplay.getWidth();
+ mInitialDisplayHeight = mDisplay.getHeight();
+ mInputManager.setDisplaySize(0, Display.unmapDisplaySize(mInitialDisplayWidth),
+ Display.unmapDisplaySize(mInitialDisplayHeight));
+ }
+
+ try {
+ mActivityManager.updateConfiguration(null);
+ } catch (RemoteException e) {
+ }
+
mPolicy.systemReady();
}
@@ -5685,9 +6492,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void setInsets(IWindow window, int touchableInsets,
- Rect contentInsets, Rect visibleInsets) {
+ Rect contentInsets, Rect visibleInsets, Region touchableArea) {
setInsetsWindow(this, window, touchableInsets, contentInsets,
- visibleInsets);
+ visibleInsets, touchableArea);
}
public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
@@ -5726,6 +6533,134 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ /* Drag/drop */
+ public IBinder prepareDrag(IWindow window, int flags,
+ int width, int height, Surface outSurface) {
+ return prepareDragSurface(window, mSurfaceSession, flags,
+ width, height, outSurface);
+ }
+
+ public boolean performDrag(IWindow window, IBinder dragToken,
+ float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+ ClipData data) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "perform drag: win=" + window + " data=" + data);
+ }
+
+ synchronized (mWindowMap) {
+ if (mDragState == null) {
+ Slog.w(TAG, "No drag prepared");
+ throw new IllegalStateException("performDrag() without prepareDrag()");
+ }
+
+ if (dragToken != mDragState.mToken) {
+ Slog.w(TAG, "Performing mismatched drag");
+ throw new IllegalStateException("performDrag() does not match prepareDrag()");
+ }
+
+ WindowState callingWin = windowForClientLocked(null, window, false);
+ if (callingWin == null) {
+ Slog.w(TAG, "Bad requesting window " + window);
+ return false; // !!! TODO: throw here?
+ }
+
+ // !!! TODO: if input is not still focused on the initiating window, fail
+ // the drag initiation (e.g. an alarm window popped up just as the application
+ // called performDrag()
+
+ mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
+
+ // !!! TODO: extract the current touch (x, y) in screen coordinates. That
+ // will let us eliminate the (touchX,touchY) parameters from the API.
+
+ // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
+ // the actual drag event dispatch stuff in the dragstate
+
+ mDragState.register();
+ mInputMonitor.setUpdateInputWindowsNeededLw();
+ mInputMonitor.updateInputWindowsLw();
+ if (!mInputManager.transferTouchFocus(callingWin.mInputChannel,
+ mDragState.mServerChannel)) {
+ Slog.e(TAG, "Unable to transfer touch focus");
+ mDragState.unregister();
+ mDragState = null;
+ mInputMonitor.setUpdateInputWindowsNeededLw();
+ mInputMonitor.updateInputWindowsLw();
+ return false;
+ }
+
+ mDragState.mData = data;
+ mDragState.mCurrentX = touchX;
+ mDragState.mCurrentY = touchY;
+ mDragState.broadcastDragStartedLw(touchX, touchY);
+
+ // remember the thumb offsets for later
+ mDragState.mThumbOffsetX = thumbCenterX;
+ mDragState.mThumbOffsetY = thumbCenterY;
+
+ // Make the surface visible at the proper location
+ final Surface surface = mDragState.mSurface;
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION performDrag");
+ Surface.openTransaction();
+ try {
+ surface.setPosition((int)(touchX - thumbCenterX),
+ (int)(touchY - thumbCenterY));
+ surface.setAlpha(.7071f);
+ surface.setLayer(mDragState.getDragLayerLw());
+ surface.show();
+ } finally {
+ Surface.closeTransaction();
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION performDrag");
+ }
+ }
+
+ return true; // success!
+ }
+
+ public void reportDropResult(IWindow window, boolean consumed) {
+ IBinder token = window.asBinder();
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "Drop result=" + consumed + " reported by " + token);
+ }
+
+ synchronized (mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (mDragState == null || mDragState.mToken != token) {
+ Slog.w(TAG, "Invalid drop-result claim by " + window);
+ throw new IllegalStateException("reportDropResult() by non-recipient");
+ }
+
+ // The right window has responded, even if it's no longer around,
+ // so be sure to halt the timeout even if the later WindowState
+ // lookup fails.
+ mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
+ WindowState callingWin = windowForClientLocked(null, window, false);
+ if (callingWin == null) {
+ Slog.w(TAG, "Bad result-reporting window " + window);
+ return; // !!! TODO: throw here?
+ }
+
+ mDragState.mDragResult = consumed;
+ mDragState.endDragLw();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void dragRecipientEntered(IWindow window) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "Drag into new candidate view @ " + window.asBinder());
+ }
+ }
+
+ public void dragRecipientExited(IWindow window) {
+ if (DEBUG_DRAG) {
+ Slog.d(TAG, "Drag from old candidate view @ " + window.asBinder());
+ }
+ }
+
public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
synchronized(mWindowMap) {
long ident = Binder.clearCallingIdentity();
@@ -5900,6 +6835,11 @@ public class WindowManagerService extends IWindowManager.Stub
final Rect mGivenVisibleInsets = new Rect();
/**
+ * This is the given touchable area relative to the window frame, or null if none.
+ */
+ final Region mGivenTouchableRegion = new Region();
+
+ /**
* Flag indicating whether the touchable region should be adjusted by
* the visible insets; if false the area outside the visible insets is
* NOT touchable, so we must use those to adjust the frame during hit
@@ -5908,6 +6848,7 @@ public class WindowManagerService extends IWindowManager.Stub
int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
// Current transformation being applied.
+ boolean mHaveMatrix;
float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
float mHScale=1, mVScale=1;
@@ -5921,8 +6862,11 @@ public class WindowManagerService extends IWindowManager.Stub
final Rect mContainingFrame = new Rect();
final Rect mDisplayFrame = new Rect();
final Rect mContentFrame = new Rect();
+ final Rect mParentFrame = new Rect();
final Rect mVisibleFrame = new Rect();
+ boolean mContentChanged;
+
float mShownAlpha = 1;
float mAlpha = 1;
float mLastAlpha = 1;
@@ -5994,13 +6938,18 @@ public class WindowManagerService extends IWindowManager.Stub
// Is this window now (or just being) removed?
boolean mRemoved;
+ // Temp for keeping track of windows that have been removed when
+ // rebuilding window list.
+ boolean mRebuilding;
+
// For debugging, this is the last information given to the surface flinger.
boolean mSurfaceShown;
int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
int mSurfaceLayer;
float mSurfaceAlpha;
- // Input channel
+ // Input channel and input window handle used by the input dispatcher.
+ InputWindowHandle mInputWindowHandle;
InputChannel mInputChannel;
// Used to improve performance of toString()
@@ -6045,6 +6994,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
mAttachedWindow = attachedWindow;
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + mAttachedWindow);
mAttachedWindow.mChildWindows.add(this);
mLayoutAttached = mAttrs.type !=
WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -6092,6 +7042,8 @@ public class WindowManagerService extends IWindowManager.Stub
mLayer = 0;
mAnimLayer = 0;
mLastLayer = 0;
+ mInputWindowHandle = new InputWindowHandle(
+ mAppToken != null ? mAppToken.mInputApplicationHandle : null, this);
}
void attach() {
@@ -6129,6 +7081,13 @@ public class WindowManagerService extends IWindowManager.Stub
h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight;
}
+ if (!mParentFrame.equals(pf)) {
+ //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame
+ // + " to " + pf);
+ mParentFrame.set(pf);
+ mContentChanged = true;
+ }
+
final Rect content = mContentFrame;
content.set(cf);
@@ -6260,6 +7219,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mAnimation != null) {
mAnimating = true;
mLocalAnimating = false;
+ mAnimation.cancel();
mAnimation = null;
}
}
@@ -6276,9 +7236,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
int flags = 0;
- if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {
- flags |= Surface.PUSH_BUFFERS;
- }
if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
flags |= Surface.SECURE;
@@ -6312,10 +7269,16 @@ public class WindowManagerService extends IWindowManager.Stub
mSurfaceW = w;
mSurfaceH = h;
try {
+ final boolean isHwAccelerated = (mAttrs.flags &
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
+ final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format;
+ if (isHwAccelerated && mAttrs.format == PixelFormat.OPAQUE) {
+ flags |= Surface.OPAQUE;
+ }
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
mAttrs.getTitle().toString(),
- 0, w, h, mAttrs.format, flags);
+ 0, w, h, format, flags);
if (SHOW_TRANSACTIONS) Slog.i(TAG, " CREATE SURFACE "
+ mSurface + " IN SESSION "
+ mSession.mSurfaceSession
@@ -6337,9 +7300,8 @@ public class WindowManagerService extends IWindowManager.Stub
+ ", set left=" + mFrame.left + " top=" + mFrame.top
+ ", animLayer=" + mAnimLayer);
if (SHOW_TRANSACTIONS) {
- Slog.i(TAG, ">>> OPEN TRANSACTION");
- if (SHOW_TRANSACTIONS) logSurface(this,
- "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" +
+ Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
+ logSurface(this, "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" +
mFrame.width() + "x" + mFrame.height() + "), layer=" +
mAnimLayer + " HIDE", null);
}
@@ -6364,8 +7326,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
mLastHidden = true;
} finally {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION");
Surface.closeTransaction();
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION createSurfaceLocked");
}
if (localLOGV) Slog.v(
TAG, "Created surface " + this);
@@ -6525,6 +7487,7 @@ public class WindowManagerService extends IWindowManager.Stub
// starting window, so there is no need for it to also
// be doing its own stuff.
if (mAnimation != null) {
+ mAnimation.cancel();
mAnimation = null;
// Make sure we clean up the animation.
mAnimating = true;
@@ -6570,7 +7533,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_ANIM) Slog.v(
TAG, "Finished animation in " + this +
" @ " + currentTime);
- mAnimation = null;
+
+ if (mAnimation != null) {
+ mAnimation.cancel();
+ mAnimation = null;
+ }
//WindowManagerService.this.dump();
}
mHasLocalTransformation = false;
@@ -6599,6 +7566,7 @@ public class WindowManagerService extends IWindowManager.Stub
// clear it and make sure we run the cleanup code.
mAnimating = true;
mLocalAnimating = true;
+ mAnimation.cancel();
mAnimation = null;
}
@@ -6613,7 +7581,10 @@ public class WindowManagerService extends IWindowManager.Stub
mAnimating = false;
mLocalAnimating = false;
- mAnimation = null;
+ if (mAnimation != null) {
+ mAnimation.cancel();
+ mAnimation = null;
+ }
mAnimLayer = mLayer;
if (mIsImWindow) {
mAnimLayer += mInputMethodAnimLayerAdjustment;
@@ -6743,8 +7714,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ final boolean screenAnimation = mScreenRotationAnimation != null
+ && mScreenRotationAnimation.isAnimating();
if (selfTransformation || attachedTransformation != null
- || appTransformation != null) {
+ || appTransformation != null || screenAnimation) {
// cache often used attributes locally
final Rect frame = mFrame;
final float tmpFloats[] = mTmpFloats;
@@ -6762,6 +7735,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (appTransformation != null) {
tmpMatrix.postConcat(appTransformation.getMatrix());
}
+ if (screenAnimation) {
+ tmpMatrix.postConcat(
+ mScreenRotationAnimation.getEnterTransformation().getMatrix());
+ }
// "convert" it into SurfaceFlinger's format
// (a 2x2 matrix + an offset)
@@ -6769,10 +7746,11 @@ public class WindowManagerService extends IWindowManager.Stub
// since it is already included in the transformation.
//Slog.i(TAG, "Transform: " + matrix);
+ mHaveMatrix = true;
tmpMatrix.getValues(tmpFloats);
mDsDx = tmpFloats[Matrix.MSCALE_X];
- mDtDx = tmpFloats[Matrix.MSKEW_X];
- mDsDy = tmpFloats[Matrix.MSKEW_Y];
+ mDtDx = tmpFloats[Matrix.MSKEW_Y];
+ mDsDy = tmpFloats[Matrix.MSKEW_X];
mDtDy = tmpFloats[Matrix.MSCALE_Y];
int x = (int)tmpFloats[Matrix.MTRANS_X] + mXOffset;
int y = (int)tmpFloats[Matrix.MTRANS_Y] + mYOffset;
@@ -6800,6 +7778,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (appTransformation != null) {
mShownAlpha *= appTransformation.getAlpha();
}
+ if (screenAnimation) {
+ mShownAlpha *=
+ mScreenRotationAnimation.getEnterTransformation().getAlpha();
+ }
} else {
//Slog.i(TAG, "Not applying alpha transform");
}
@@ -6816,6 +7798,7 @@ public class WindowManagerService extends IWindowManager.Stub
mShownFrame.offset(mXOffset, mYOffset);
}
mShownAlpha = mAlpha;
+ mHaveMatrix = false;
mDsDx = 1;
mDtDx = 0;
mDsDy = 0;
@@ -6846,7 +7829,7 @@ public class WindowManagerService extends IWindowManager.Stub
final AppWindowToken atoken = mAppToken;
return mSurface != null && !mAttachedHidden
&& (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
- && (mOrientationChanging || (!mDrawPending && !mCommitDrawPending))
+ && !mDrawPending && !mCommitDrawPending
&& !mExiting && !mDestroying;
}
@@ -6872,6 +7855,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
+ * Can this window possibly be a drag/drop target? The test here is
+ * a combination of the above "visible now" with the check that the
+ * Input Manager uses when discarding windows from input consideration.
+ */
+ boolean isPotentialDragTarget() {
+ return isVisibleNow() && (mInputChannel != null) && !mRemoved;
+ }
+
+ /**
* Same as isVisible(), but we also count it as visible between the
* call to IWindowSession.add() and the first relayout().
*/
@@ -6950,14 +7942,12 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Returns true if the window has a surface that it has drawn a
- * complete UI in to. Note that this returns true if the orientation
- * is changing even if the window hasn't redrawn because we don't want
- * to stop things from executing during that time.
+ * complete UI in to.
*/
public boolean isDrawnLw() {
final AppWindowToken atoken = mAppToken;
return mSurface != null && !mDestroying
- && (mOrientationChanging || (!mDrawPending && !mCommitDrawPending));
+ && !mDrawPending && !mCommitDrawPending;
}
/**
@@ -6972,6 +7962,19 @@ public class WindowManagerService extends IWindowManager.Stub
&& !mDrawPending && !mCommitDrawPending;
}
+ /**
+ * Return whether this window is wanting to have a translation
+ * animation applied to it for an in-progress move. (Only makes
+ * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
+ */
+ boolean shouldAnimateMove() {
+ return mContentChanged && !mExiting && !mLastHidden && !mDisplayFrozen
+ && (mFrame.top != mLastFrame.top
+ || mFrame.left != mLastFrame.left)
+ && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove())
+ && mPolicy.isScreenOn();
+ }
+
boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
return
// only if the application is requesting compatible window
@@ -6996,6 +7999,7 @@ public class WindowManagerService extends IWindowManager.Stub
disposeInputChannel();
if (mAttachedWindow != null) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing " + this + " from " + mAttachedWindow);
mAttachedWindow.mChildWindows.remove(this);
}
destroySurfaceLocked();
@@ -7118,6 +8122,36 @@ public class WindowManagerService extends IWindowManager.Stub
return true;
}
+ public void getTouchableRegion(Region outRegion) {
+ final Rect frame = mFrame;
+ switch (mTouchableInsets) {
+ default:
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ outRegion.set(frame);
+ break;
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
+ final Rect inset = mGivenContentInsets;
+ outRegion.set(
+ frame.left + inset.left, frame.top + inset.top,
+ frame.right - inset.right, frame.bottom - inset.bottom);
+ break;
+ }
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
+ final Rect inset = mGivenVisibleInsets;
+ outRegion.set(
+ frame.left + inset.left, frame.top + inset.top,
+ frame.right - inset.right, frame.bottom - inset.bottom);
+ break;
+ }
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: {
+ final Region givenTouchableRegion = mGivenTouchableRegion;
+ outRegion.set(givenTouchableRegion);
+ outRegion.translate(frame.left, frame.top);
+ break;
+ }
+ }
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mSession="); pw.print(mSession);
pw.print(" mClient="); pw.println(mClient.asBinder());
@@ -7198,6 +8232,8 @@ public class WindowManagerService extends IWindowManager.Stub
pw.println();
pw.print(prefix); pw.print("mContainingFrame=");
mContainingFrame.printShortString(pw);
+ pw.print(" mParentFrame=");
+ mParentFrame.printShortString(pw);
pw.print(" mDisplayFrame=");
mDisplayFrame.printShortString(pw);
pw.println();
@@ -7209,11 +8245,6 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mVisibleInsets="); mVisibleInsets.printShortString(pw);
pw.print(" last="); mLastVisibleInsets.printShortString(pw);
pw.println();
- if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
- pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
- pw.print(" mAlpha="); pw.print(mAlpha);
- pw.print(" mLastAlpha="); pw.println(mLastAlpha);
- }
if (mAnimating || mLocalAnimating || mAnimationIsEntrance
|| mAnimation != null) {
pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
@@ -7228,6 +8259,17 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" "); mTransformation.printShortString(pw);
pw.println();
}
+ if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
+ pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
+ pw.print(" mAlpha="); pw.print(mAlpha);
+ pw.print(" mLastAlpha="); pw.println(mLastAlpha);
+ }
+ if (mHaveMatrix) {
+ pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx);
+ pw.print(" mDtDx="); pw.print(mDtDx);
+ pw.print(" mDsDy="); pw.print(mDsDy);
+ pw.print(" mDtDy="); pw.println(mDtDy);
+ }
pw.print(prefix); pw.print("mDrawPending="); pw.print(mDrawPending);
pw.print(" mCommitDrawPending="); pw.print(mCommitDrawPending);
pw.print(" mReadyToShow="); pw.print(mReadyToShow);
@@ -7421,11 +8463,15 @@ public class WindowManagerService extends IWindowManager.Stub
boolean startingMoved;
boolean firstWindowDrawn;
+ // Input application handle used by the input dispatcher.
+ InputApplicationHandle mInputApplicationHandle;
+
AppWindowToken(IApplicationToken _token) {
super(_token.asBinder(),
WindowManager.LayoutParams.TYPE_APPLICATION, true);
appWindowToken = this;
appToken = _token;
+ mInputApplicationHandle = new InputApplicationHandle(this);
}
public void setAnimation(Animation anim) {
@@ -7601,7 +8647,8 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState win = allAppWindows.get(i);
if (win == startingWindow || win.mAppFreezing
|| win.mViewVisibility != View.VISIBLE
- || win.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ || win.mAttrs.type == TYPE_APPLICATION_STARTING
+ || win.mDestroying) {
continue;
}
if (DEBUG_VISIBILITY) {
@@ -7692,14 +8739,14 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(prefix); pw.print("animating="); pw.print(animating);
pw.print(" animation="); pw.println(animation);
}
- if (animLayerAdjustment != 0) {
- pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment);
- }
if (hasTransformation) {
- pw.print(prefix); pw.print("hasTransformation="); pw.print(hasTransformation);
- pw.print(" transformation="); transformation.printShortString(pw);
+ pw.print(prefix); pw.print("XForm: ");
+ transformation.printShortString(pw);
pw.println();
}
+ if (animLayerAdjustment != 0) {
+ pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment);
+ }
if (startingData != null || removed || firstWindowDrawn) {
pw.print(prefix); pw.print("startingData="); pw.print(startingData);
pw.print(" removed="); pw.print(removed);
@@ -7751,14 +8798,16 @@ public class WindowManagerService extends IWindowManager.Stub
final CharSequence nonLocalizedLabel;
final int labelRes;
final int icon;
+ final int windowFlags;
StartingData(String _pkg, int _theme, CharSequence _nonLocalizedLabel,
- int _labelRes, int _icon) {
+ int _labelRes, int _icon, int _windowFlags) {
pkg = _pkg;
theme = _theme;
nonLocalizedLabel = _nonLocalizedLabel;
labelRes = _labelRes;
icon = _icon;
+ windowFlags = _windowFlags;
}
}
@@ -7779,6 +8828,8 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int APP_FREEZE_TIMEOUT = 17;
public static final int SEND_NEW_CONFIGURATION = 18;
public static final int REPORT_WINDOWS_CHANGE = 19;
+ public static final int DRAG_START_TIMEOUT = 20;
+ public static final int DRAG_END_TIMEOUT = 21;
private Session mLastReportedHold;
@@ -7831,6 +8882,8 @@ public class WindowManagerService extends IWindowManager.Stub
// Ignore if process has died.
}
}
+
+ mPolicy.focusChanged(lastFocus, newFocus);
}
} break;
@@ -7877,7 +8930,7 @@ public class WindowManagerService extends IWindowManager.Stub
view = mPolicy.addStartingWindow(
wtoken.token, sd.pkg,
sd.theme, sd.nonLocalizedLabel, sd.labelRes,
- sd.icon);
+ sd.icon, sd.windowFlags);
} catch (Exception e) {
Slog.w(TAG, "Exception when adding starting window", e);
}
@@ -8121,6 +9174,36 @@ public class WindowManagerService extends IWindowManager.Stub
break;
}
+ case DRAG_START_TIMEOUT: {
+ IBinder win = (IBinder)msg.obj;
+ if (DEBUG_DRAG) {
+ Slog.w(TAG, "Timeout starting drag by win " + win);
+ }
+ synchronized (mWindowMap) {
+ // !!! TODO: ANR the app that has failed to start the drag in time
+ if (mDragState != null) {
+ mDragState.unregister();
+ mInputMonitor.setUpdateInputWindowsNeededLw();
+ mInputMonitor.updateInputWindowsLw();
+ mDragState.reset();
+ mDragState = null;
+ }
+ }
+ break;
+ }
+
+ case DRAG_END_TIMEOUT: {
+ IBinder win = (IBinder)msg.obj;
+ if (DEBUG_DRAG) {
+ Slog.w(TAG, "Timeout ending drag to win " + win);
+ }
+ synchronized (mWindowMap) {
+ // !!! TODO: ANR the drag-receiving app
+ mDragState.mDragResult = false;
+ mDragState.endDragLw();
+ }
+ break;
+ }
}
}
}
@@ -8199,12 +9282,18 @@ public class WindowManagerService extends IWindowManager.Stub
int lastWallpaper = -1;
int numRemoved = 0;
+ if (mRebuildTmp.length < NW) {
+ mRebuildTmp = new WindowState[NW+10];
+ }
+
// First remove all existing app windows.
i=0;
while (i < NW) {
WindowState w = mWindows.get(i);
if (w.mAppToken != null) {
WindowState win = mWindows.remove(i);
+ win.mRebuilding = true;
+ mRebuildTmp[numRemoved] = win;
mWindowsChanged = true;
if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
"Rebuild removing window: " + win);
@@ -8242,6 +9331,21 @@ public class WindowManagerService extends IWindowManager.Stub
if (i != numRemoved) {
Slog.w(TAG, "Rebuild removed " + numRemoved
+ " windows but added " + i);
+ for (i=0; i<numRemoved; i++) {
+ WindowState ws = mRebuildTmp[i];
+ if (ws.mRebuilding) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ ws.dump(pw, "");
+ pw.flush();
+ Slog.w(TAG, "This window was lost: " + ws);
+ Slog.w(TAG, sw.toString());
+ }
+ }
+ Slog.w(TAG, "Current app token list:");
+ dumpAppTokensLocked();
+ Slog.w(TAG, "Final window list:");
+ dumpWindowsLocked();
}
}
@@ -8251,6 +9355,12 @@ public class WindowManagerService extends IWindowManager.Stub
int curLayer = 0;
int i;
+ if (DEBUG_LAYERS) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Log.v(TAG, "Assigning layers", here);
+ }
+
for (i=0; i<N; i++) {
WindowState w = mWindows.get(i);
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
@@ -8302,38 +9412,46 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
+ mInLayout = true;
boolean recoveringMemory = false;
- if (mForceRemoves != null) {
- recoveringMemory = true;
- // Wait a little it for things to settle down, and off we go.
- for (int i=0; i<mForceRemoves.size(); i++) {
- WindowState ws = mForceRemoves.get(i);
- Slog.i(TAG, "Force removing: " + ws);
- removeWindowInnerLocked(ws.mSession, ws);
- }
- mForceRemoves = null;
- Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
- Object tmp = new Object();
- synchronized (tmp) {
- try {
- tmp.wait(250);
- } catch (InterruptedException e) {
+
+ try {
+ if (mForceRemoves != null) {
+ recoveringMemory = true;
+ // Wait a little bit for things to settle down, and off we go.
+ for (int i=0; i<mForceRemoves.size(); i++) {
+ WindowState ws = mForceRemoves.get(i);
+ Slog.i(TAG, "Force removing: " + ws);
+ removeWindowInnerLocked(ws.mSession, ws);
+ }
+ mForceRemoves = null;
+ Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
+ Object tmp = new Object();
+ synchronized (tmp) {
+ try {
+ tmp.wait(250);
+ } catch (InterruptedException e) {
+ }
}
}
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Unhandled exception while force removing for memory", e);
}
-
- mInLayout = true;
+
try {
performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
- int i = mPendingRemove.size()-1;
- if (i >= 0) {
- while (i >= 0) {
- WindowState w = mPendingRemove.get(i);
- removeWindowInnerLocked(w.mSession, w);
- i--;
+ int N = mPendingRemove.size();
+ if (N > 0) {
+ if (mPendingRemoveTmp.length < N) {
+ mPendingRemoveTmp = new WindowState[N+10];
}
+ mPendingRemove.toArray(mPendingRemoveTmp);
mPendingRemove.clear();
+ for (int i=0; i<N; i++) {
+ WindowState w = mPendingRemoveTmp[i];
+ removeWindowInnerLocked(w.mSession, w);
+ }
mInLayout = false;
assignLayersLocked();
@@ -8356,7 +9474,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- private final int performLayoutLockedInner() {
+ private final int performLayoutLockedInner(boolean initial, boolean updateInputWindows) {
if (!mLayoutNeeded) {
return 0;
}
@@ -8390,16 +9508,16 @@ public class WindowManagerService extends IWindowManager.Stub
final AppWindowToken atoken = win.mAppToken;
final boolean gone = win.mViewVisibility == View.GONE
|| !win.mRelayoutCalled
- || win.mRootToken.hidden
+ || (atoken == null && win.mRootToken.hidden)
|| (atoken != null && atoken.hiddenRequested)
|| win.mAttachedHidden
|| win.mExiting || win.mDestroying;
- if (!win.mLayoutAttached) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "First pass " + win
+ if (DEBUG_LAYOUT && !win.mLayoutAttached) {
+ Slog.v(TAG, "First pass " + win
+ ": gone=" + gone + " mHaveFrame=" + win.mHaveFrame
+ " mLayoutAttached=" + win.mLayoutAttached);
- if (DEBUG_LAYOUT && gone) Slog.v(TAG, " (mViewVisibility="
+ if (gone) Slog.v(TAG, " (mViewVisibility="
+ win.mViewVisibility + " mRelayoutCalled="
+ win.mRelayoutCalled + " hidden="
+ win.mRootToken.hidden + " hiddenRequested="
@@ -8414,6 +9532,10 @@ public class WindowManagerService extends IWindowManager.Stub
// just don't display").
if (!gone || !win.mHaveFrame) {
if (!win.mLayoutAttached) {
+ if (initial) {
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
+ win.mContentChanged = false;
+ }
mPolicy.layoutWindowLw(win, win.mAttrs, null);
win.mLayoutSeq = seq;
if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
@@ -8433,18 +9555,22 @@ public class WindowManagerService extends IWindowManager.Stub
for (i = topAttached; i >= 0; i--) {
WindowState win = mWindows.get(i);
- // If this view is GONE, then skip it -- keep the current
- // frame, and let the caller know so they can ignore it
- // if they want. (We do the normal layout for INVISIBLE
- // windows, since that means "perform layout as normal,
- // just don't display").
if (win.mLayoutAttached) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Second pass " + win
+ " mHaveFrame=" + win.mHaveFrame
+ " mViewVisibility=" + win.mViewVisibility
+ " mRelayoutCalled=" + win.mRelayoutCalled);
+ // If this view is GONE, then skip it -- keep the current
+ // frame, and let the caller know so they can ignore it
+ // if they want. (We do the normal layout for INVISIBLE
+ // windows, since that means "perform layout as normal,
+ // just don't display").
if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
|| !win.mHaveFrame) {
+ if (initial) {
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
+ win.mContentChanged = false;
+ }
mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
win.mLayoutSeq = seq;
if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
@@ -8456,13 +9582,22 @@ public class WindowManagerService extends IWindowManager.Stub
}
// Window frames may have changed. Tell the input dispatcher about it.
- mInputMonitor.updateInputWindowsLw();
+ mInputMonitor.setUpdateInputWindowsNeededLw();
+ if (updateInputWindows) {
+ mInputMonitor.updateInputWindowsLw();
+ }
return mPolicy.finishLayoutLw();
}
+ // "Something has changed! Let's make it correct now."
private final void performLayoutAndPlaceSurfacesLockedInner(
boolean recoveringMemory) {
+ if (mDisplay == null) {
+ Slog.i(TAG, "skipping performLayoutAndPlaceSurfacesLockedInner with no mDisplay");
+ return;
+ }
+
final long currentTime = SystemClock.uptimeMillis();
final int dw = mDisplay.getWidth();
final int dh = mDisplay.getHeight();
@@ -8471,7 +9606,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (mFocusMayChange) {
mFocusMayChange = false;
- updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+ updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/);
}
// Initialize state of exiting tokens.
@@ -8491,13 +9627,14 @@ public class WindowManagerService extends IWindowManager.Stub
boolean focusDisplayed = false;
boolean animating = false;
boolean createWatermark = false;
+ boolean updateRotation = false;
if (mFxSession == null) {
mFxSession = new SurfaceSession();
createWatermark = true;
}
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
Surface.openTransaction();
@@ -8507,6 +9644,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (mWatermark != null) {
mWatermark.positionSurface(dw, dh);
}
+ if (mStrictModeFlash != null) {
+ mStrictModeFlash.positionSurface(dw, dh);
+ }
try {
boolean wallpaperForceHidingChanged = false;
@@ -8532,7 +9672,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
- if (updateOrientationFromAppTokensLocked()) {
+ if (updateOrientationFromAppTokensLocked(true)) {
mLayoutNeeded = true;
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
@@ -8544,7 +9684,7 @@ public class WindowManagerService extends IWindowManager.Stub
// FIRST LOOP: Perform a layout, if needed.
if (repeats < 4) {
- changes = performLayoutLockedInner();
+ changes = performLayoutLockedInner(repeats == 0, false /*updateInputWindows*/);
if (changes != 0) {
continue;
}
@@ -8579,9 +9719,21 @@ public class WindowManagerService extends IWindowManager.Stub
animating = tokensAnimating;
+ if (mScreenRotationAnimation != null) {
+ if (mScreenRotationAnimation.isAnimating()) {
+ if (mScreenRotationAnimation.stepAnimation(currentTime)) {
+ animating = true;
+ } else {
+ mScreenRotationAnimation = null;
+ updateRotation = true;
+ }
+ }
+ }
+
boolean tokenMayBeDrawn = false;
boolean wallpaperMayChange = false;
boolean forceHiding = false;
+ WindowState windowDetachedWallpaper = null;
mPolicy.beginAnimationLw(dw, dh);
@@ -8593,7 +9745,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowManager.LayoutParams attrs = w.mAttrs;
if (w.mSurface != null) {
- // Execute animation.
+ // Take care of the window being ready to display.
if (w.commitFinishDrawingLocked(currentTime)) {
if ((w.mAttrs.flags
& WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
@@ -8603,19 +9755,56 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- boolean wasAnimating = w.mAnimating;
- if (w.stepAnimationLocked(currentTime, dw, dh)) {
+ final boolean wasAnimating = w.mAnimating;
+
+ int animDw = dw;
+ int animDh = dh;
+
+ // If the window has moved due to its containing
+ // content frame changing, then we'd like to animate
+ // it. The checks here are ordered by what is least
+ // likely to be true first.
+ if (w.shouldAnimateMove()) {
+ // Frame has moved, containing content frame
+ // has also moved, and we're not currently animating...
+ // let's do something.
+ Animation a = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.window_move_from_decor);
+ w.setAnimation(a);
+ animDw = w.mLastFrame.left - w.mFrame.left;
+ animDh = w.mLastFrame.top - w.mFrame.top;
+ }
+
+ // Execute animation.
+ final boolean nowAnimating = w.stepAnimationLocked(currentTime,
+ animDw, animDh);
+
+ // If this window is animating, make a note that we have
+ // an animating window and take care of a request to run
+ // a detached wallpaper animation.
+ if (nowAnimating) {
+ if (w.mAnimation != null && w.mAnimation.getDetachWallpaper()) {
+ windowDetachedWallpaper = w;
+ }
animating = true;
- //w.dump(" ");
}
+
+ // If this window's app token is running a detached wallpaper
+ // animation, make a note so we can ensure the wallpaper is
+ // displayed behind it.
+ if (w.mAppToken != null && w.mAppToken.animation != null
+ && w.mAppToken.animation.getDetachWallpaper()) {
+ windowDetachedWallpaper = w;
+ }
+
if (wasAnimating && !w.mAnimating && mWallpaperTarget == w) {
wallpaperMayChange = true;
}
if (mPolicy.doesForceHide(w, attrs)) {
- if (!wasAnimating && animating) {
+ if (!wasAnimating && nowAnimating) {
if (DEBUG_VISIBILITY) Slog.v(TAG,
- "Animation done that could impact force hide: "
+ "Animation started that could impact force hide: "
+ w);
wallpaperForceHidingChanged = true;
mFocusMayChange = true;
@@ -8712,12 +9901,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (tokenMayBeDrawn) {
// See if any windows have been drawn, so they (and others
// associated with them) can now be shown.
- final int NT = mTokenList.size();
+ final int NT = mAppTokens.size();
for (i=0; i<NT; i++) {
- AppWindowToken wtoken = mTokenList.get(i).appWindowToken;
- if (wtoken == null) {
- continue;
- }
+ AppWindowToken wtoken = mAppTokens.get(i);
if (wtoken.freezingScreen) {
int numInteresting = wtoken.numInterestingWindows;
if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
@@ -8814,8 +10000,8 @@ public class WindowManagerService extends IWindowManager.Stub
// The top-most window will supply the layout params,
// and we will determine it below.
LayoutParams animLp = null;
- AppWindowToken animToken = null;
int bestAnimLayer = -1;
+ boolean fullscreenAnim = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New wallpaper target=" + mWallpaperTarget
@@ -8857,11 +10043,18 @@ public class WindowManagerService extends IWindowManager.Stub
// window, we will always use its anim.
if ((ws.mAttrs.flags&FLAG_COMPATIBLE_WINDOW) != 0) {
animLp = ws.mAttrs;
- animToken = ws.mAppToken;
bestAnimLayer = Integer.MAX_VALUE;
- } else if (ws.mLayer > bestAnimLayer) {
+ } else if (!fullscreenAnim || ws.mLayer > bestAnimLayer) {
+ animLp = ws.mAttrs;
+ bestAnimLayer = ws.mLayer;
+ }
+ fullscreenAnim = true;
+ }
+ } else if (!fullscreenAnim) {
+ WindowState ws = wtoken.findMainWindow();
+ if (ws != null) {
+ if (ws.mLayer > bestAnimLayer) {
animLp = ws.mAttrs;
- animToken = ws.mAppToken;
bestAnimLayer = ws.mLayer;
}
}
@@ -8899,15 +10092,6 @@ public class WindowManagerService extends IWindowManager.Stub
"New transit into wallpaper: " + transit);
}
- if ((transit&WindowManagerPolicy.TRANSIT_ENTER_MASK) != 0) {
- mLastEnterAnimToken = animToken;
- mLastEnterAnimParams = animLp;
- } else if (mLastEnterAnimParams != null) {
- animLp = mLastEnterAnimParams;
- mLastEnterAnimToken = null;
- mLastEnterAnimParams = null;
- }
-
// If all closing windows are obscured, then there is
// no need to do an animation. This is the case, for
// example, when this transition is being done behind
@@ -8952,12 +10136,14 @@ public class WindowManagerService extends IWindowManager.Stub
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
- changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+ changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT
+ | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
mLayoutNeeded = true;
if (!moveInputMethodWindowsIfNeededLocked(true)) {
assignLayersLocked();
}
- updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES);
+ updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+ false /*updateInputWindows*/);
mFocusMayChange = false;
}
}
@@ -9039,6 +10225,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ if (mWindowDetachedWallpaper != windowDetachedWallpaper) {
+ if (DEBUG_WALLPAPER) Slog.v(TAG,
+ "Detached wallpaper changed from " + mWindowDetachedWallpaper
+ + windowDetachedWallpaper);
+ mWindowDetachedWallpaper = windowDetachedWallpaper;
+ wallpaperMayChange = true;
+ }
+
if (wallpaperMayChange) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper may change! Adjusting");
@@ -9058,7 +10252,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (mFocusMayChange) {
mFocusMayChange = false;
- if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES)) {
+ if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+ false /*updateInputWindows*/)) {
changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
adjResult = 0;
}
@@ -9070,8 +10265,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
+ Integer.toHexString(changes));
-
- mInputMonitor.updateInputWindowsLw();
} while (changes != 0);
// THIRD LOOP: Update the surfaces of all windows.
@@ -9115,11 +10308,8 @@ public class WindowManagerService extends IWindowManager.Stub
+ ": new=" + w.mShownFrame + ", old="
+ w.mLastShownFrame);
- boolean resize;
int width, height;
if ((w.mAttrs.flags & w.mAttrs.FLAG_SCALED) != 0) {
- resize = w.mLastRequestedWidth != w.mRequestedWidth ||
- w.mLastRequestedHeight != w.mRequestedHeight;
// for a scaled surface, we just want to use
// the requested size.
width = w.mRequestedWidth;
@@ -9127,58 +10317,61 @@ public class WindowManagerService extends IWindowManager.Stub
w.mLastRequestedWidth = width;
w.mLastRequestedHeight = height;
w.mLastShownFrame.set(w.mShownFrame);
- try {
- if (SHOW_TRANSACTIONS) logSurface(w,
- "POS " + w.mShownFrame.left
- + ", " + w.mShownFrame.top, null);
- w.mSurfaceX = w.mShownFrame.left;
- w.mSurfaceY = w.mShownFrame.top;
- w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error positioning surface in " + w, e);
- if (!recoveringMemory) {
- reclaimSomeSurfaceMemoryLocked(w, "position");
- }
- }
} else {
- resize = !w.mLastShownFrame.equals(w.mShownFrame);
width = w.mShownFrame.width();
height = w.mShownFrame.height();
w.mLastShownFrame.set(w.mShownFrame);
}
- if (resize) {
- if (width < 1) width = 1;
- if (height < 1) height = 1;
- if (w.mSurface != null) {
+ if (w.mSurface != null) {
+ if (w.mSurfaceX != w.mShownFrame.left
+ || w.mSurfaceY != w.mShownFrame.top) {
try {
if (SHOW_TRANSACTIONS) logSurface(w,
- "POS " + w.mShownFrame.left + ","
- + w.mShownFrame.top + " SIZE "
- + w.mShownFrame.width() + "x"
+ "POS " + w.mShownFrame.left
+ + ", " + w.mShownFrame.top, null);
+ w.mSurfaceX = w.mShownFrame.left;
+ w.mSurfaceY = w.mShownFrame.top;
+ w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error positioning surface of " + w
+ + " pos=(" + w.mShownFrame.left
+ + "," + w.mShownFrame.top + ")", e);
+ if (!recoveringMemory) {
+ reclaimSomeSurfaceMemoryLocked(w, "position");
+ }
+ }
+ }
+
+ if (width < 1) {
+ width = 1;
+ }
+ if (height < 1) {
+ height = 1;
+ }
+
+ if (w.mSurfaceW != width || w.mSurfaceH != height) {
+ try {
+ if (SHOW_TRANSACTIONS) logSurface(w,
+ "SIZE " + w.mShownFrame.width() + "x"
+ w.mShownFrame.height(), null);
w.mSurfaceResized = true;
w.mSurfaceW = width;
w.mSurfaceH = height;
w.mSurface.setSize(width, height);
- w.mSurfaceX = w.mShownFrame.left;
- w.mSurfaceY = w.mShownFrame.top;
- w.mSurface.setPosition(w.mShownFrame.left,
- w.mShownFrame.top);
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
// as running out of memory), don't take down the
// entire system.
- Slog.e(TAG, "Failure updating surface of " + w
- + "size=(" + width + "x" + height
- + "), pos=(" + w.mShownFrame.left
- + "," + w.mShownFrame.top + ")", e);
+ Slog.e(TAG, "Error resizing surface of " + w
+ + " size=(" + width + "x" + height + ")", e);
if (!recoveringMemory) {
reclaimSomeSurfaceMemoryLocked(w, "size");
}
}
}
}
+
if (!w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) {
w.mContentInsetsChanged =
!w.mLastContentInsets.equals(w.mContentInsets);
@@ -9195,11 +10388,21 @@ public class WindowManagerService extends IWindowManager.Stub
if (localLOGV) Slog.v(TAG, "Resizing " + w
+ ": configChanged=" + configChanged
+ " last=" + w.mLastFrame + " frame=" + w.mFrame);
- if (!w.mLastFrame.equals(w.mFrame)
+ boolean frameChanged = !w.mLastFrame.equals(w.mFrame);
+ if (frameChanged
|| w.mContentInsetsChanged
|| w.mVisibleInsetsChanged
|| w.mSurfaceResized
|| configChanged) {
+ if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
+ Slog.v(TAG, "Resize reasons: "
+ + "frameChanged=" + frameChanged
+ + " contentInsetsChanged=" + w.mContentInsetsChanged
+ + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
+ + " surfaceResized=" + w.mSurfaceResized
+ + " configChanged=" + configChanged);
+ }
+
w.mLastFrame.set(w.mFrame);
w.mLastContentInsets.set(w.mContentInsets);
w.mLastVisibleInsets.set(w.mVisibleInsets);
@@ -9251,12 +10454,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (w.mAttachedHidden || !w.isReadyForDisplay()) {
if (!w.mLastHidden) {
//dump();
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Window hiding: waitingToShow="
- + w.mRootToken.waitingToShow + " polvis="
- + w.mPolicyVisibility + " atthid="
- + w.mAttachedHidden + " tokhid="
- + w.mRootToken.hidden + " vis="
- + w.mViewVisibility);
w.mLastHidden = true;
if (SHOW_TRANSACTIONS) logSurface(w,
"HIDE (performLayout)", null);
@@ -9368,6 +10565,11 @@ public class WindowManagerService extends IWindowManager.Stub
w.mOrientationChanging = false;
}
+ if (w.mContentChanged) {
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+ w.mContentChanged = false;
+ }
+
final boolean canBeSeen = w.isDisplayedLw();
if (someoneLosingFocus && w == mCurrentFocus && canBeSeen) {
@@ -9445,7 +10647,8 @@ public class WindowManagerService extends IWindowManager.Stub
mDimAnimator = new DimAnimator(mFxSession);
}
mDimAnimator.show(dw, dh);
- mDimAnimator.updateParameters(w, currentTime);
+ mDimAnimator.updateParameters(mContext.getResources(),
+ w, currentTime);
}
}
if ((attrFlags&FLAG_BLUR_BEHIND) != 0) {
@@ -9521,16 +10724,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
mBlurShown = false;
}
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION");
} catch (RuntimeException e) {
Slog.e(TAG, "Unhandled exception in Window Manager", e);
}
- mInputMonitor.updateInputWindowsLw();
-
Surface.closeTransaction();
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
+
if (mWatermark != null) {
mWatermark.drawIfNeeded();
}
@@ -9620,12 +10821,10 @@ public class WindowManagerService extends IWindowManager.Stub
// soon as their animations are complete
token.animation = null;
token.animating = false;
+ if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
+ "performLayout: App token exiting now removed" + token);
mAppTokens.remove(token);
mExitingAppTokens.remove(i);
- if (mLastEnterAnimToken == token) {
- mLastEnterAnimToken = null;
- mLastEnterAnimParams = null;
- }
}
}
@@ -9657,30 +10856,28 @@ public class WindowManagerService extends IWindowManager.Stub
} else if (animating) {
requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
}
-
+
+ // Finally update all input windows now that the window changes have stabilized.
+ mInputMonitor.setUpdateInputWindowsNeededLw();
mInputMonitor.updateInputWindowsLw();
-
- if (DEBUG_FREEZE) Slog.v(TAG, "Layout: mDisplayFrozen=" + mDisplayFrozen
- + " holdScreen=" + holdScreen);
- if (!mDisplayFrozen) {
- setHoldScreenLocked(holdScreen != null);
- if (screenBrightness < 0 || screenBrightness > 1.0f) {
- mPowerManager.setScreenBrightnessOverride(-1);
- } else {
- mPowerManager.setScreenBrightnessOverride((int)
- (screenBrightness * Power.BRIGHTNESS_ON));
- }
- if (buttonBrightness < 0 || buttonBrightness > 1.0f) {
- mPowerManager.setButtonBrightnessOverride(-1);
- } else {
- mPowerManager.setButtonBrightnessOverride((int)
- (buttonBrightness * Power.BRIGHTNESS_ON));
- }
- if (holdScreen != mHoldingScreenOn) {
- mHoldingScreenOn = holdScreen;
- Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen);
- mH.sendMessage(m);
- }
+
+ setHoldScreenLocked(holdScreen != null);
+ if (screenBrightness < 0 || screenBrightness > 1.0f) {
+ mPowerManager.setScreenBrightnessOverride(-1);
+ } else {
+ mPowerManager.setScreenBrightnessOverride((int)
+ (screenBrightness * Power.BRIGHTNESS_ON));
+ }
+ if (buttonBrightness < 0 || buttonBrightness > 1.0f) {
+ mPowerManager.setButtonBrightnessOverride(-1);
+ } else {
+ mPowerManager.setButtonBrightnessOverride((int)
+ (buttonBrightness * Power.BRIGHTNESS_ON));
+ }
+ if (holdScreen != mHoldingScreenOn) {
+ mHoldingScreenOn = holdScreen;
+ Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen);
+ mH.sendMessage(m);
}
if (mTurnOnScreen) {
@@ -9690,6 +10887,15 @@ public class WindowManagerService extends IWindowManager.Stub
mTurnOnScreen = false;
}
+ if (updateRotation) {
+ if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
+ boolean changed = setRotationUncheckedLocked(
+ WindowManagerPolicy.USE_LAST_ROTATION, 0, false);
+ if (changed) {
+ sendNewConfiguration();
+ }
+ }
+
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
@@ -9739,7 +10945,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
return true;
} catch (RuntimeException e) {
- Slog.w(TAG, "Failure showing surface " + win.mSurface + " in " + win);
+ Slog.w(TAG, "Failure showing surface " + win.mSurface + " in " + win, e);
}
reclaimSomeSurfaceMemoryLocked(win, "show");
@@ -9774,6 +10980,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ " token=" + win.mToken
+ " pid=" + ws.mSession.mPid
+ " uid=" + ws.mSession.mUid);
+ if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
ws.mSurface.destroy();
ws.mSurfaceShown = false;
ws.mSurface = null;
@@ -9781,10 +10988,11 @@ public class WindowManagerService extends IWindowManager.Stub
i--;
N--;
leakedSurface = true;
- } else if (win.mAppToken != null && win.mAppToken.clientHidden) {
+ } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
+ ws + " surface=" + ws.mSurface
+ " token=" + win.mAppToken);
+ if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
ws.mSurface.destroy();
ws.mSurfaceShown = false;
ws.mSurface = null;
@@ -9822,6 +11030,7 @@ public class WindowManagerService extends IWindowManager.Stub
// surface and ask the app to request another one.
Slog.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry.");
if (surface != null) {
+ if (SHOW_TRANSACTIONS) logSurface(win, "RECOVER DESTROY", null);
surface.destroy();
win.mSurfaceShown = false;
win.mSurface = null;
@@ -9837,7 +11046,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- private boolean updateFocusedWindowLocked(int mode) {
+ private boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
WindowState newFocus = computeFocusedWindowLocked();
if (mCurrentFocus != newFocus) {
// This check makes sure that we don't already have the focus
@@ -9858,7 +11067,7 @@ public class WindowManagerService extends IWindowManager.Stub
mLayoutNeeded = true;
}
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- performLayoutLockedInner();
+ performLayoutLockedInner(true /*initial*/, updateInputWindows);
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
// for handleNewWindowLocked() below.
@@ -9869,15 +11078,15 @@ public class WindowManagerService extends IWindowManager.Stub
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
// If we defer assigning layers, then the caller is responsible for
// doing this part.
- finishUpdateFocusedWindowAfterAssignLayersLocked();
+ finishUpdateFocusedWindowAfterAssignLayersLocked(updateInputWindows);
}
return true;
}
return false;
}
- private void finishUpdateFocusedWindowAfterAssignLayersLocked() {
- mInputMonitor.setInputFocusLw(mCurrentFocus);
+ private void finishUpdateFocusedWindowAfterAssignLayersLocked(boolean updateInputWindows) {
+ mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
}
private WindowState computeFocusedWindowLocked() {
@@ -9949,7 +11158,7 @@ public class WindowManagerService extends IWindowManager.Stub
return result;
}
- private void startFreezingDisplayLocked() {
+ private void startFreezingDisplayLocked(boolean inTransaction) {
if (mDisplayFrozen) {
return;
}
@@ -9969,8 +11178,6 @@ public class WindowManagerService extends IWindowManager.Stub
mFreezeGcPending = now;
}
- if (DEBUG_FREEZE) Slog.v(TAG, "*** FREEZING DISPLAY", new RuntimeException());
-
mDisplayFrozen = true;
mInputMonitor.freezeInputDispatchingLw();
@@ -9985,7 +11192,19 @@ public class WindowManagerService extends IWindowManager.Stub
File file = new File("/data/system/frozen");
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
}
- Surface.freezeDisplay(0);
+
+ if (CUSTOM_SCREEN_ROTATION) {
+ if (mScreenRotationAnimation != null && mScreenRotationAnimation.isAnimating()) {
+ mScreenRotationAnimation.kill();
+ mScreenRotationAnimation = null;
+ }
+ if (mScreenRotationAnimation == null) {
+ mScreenRotationAnimation = new ScreenRotationAnimation(mContext,
+ mDisplay, mFxSession, inTransaction);
+ }
+ } else {
+ Surface.freezeDisplay(0);
+ }
}
private void stopFreezingDisplayLocked() {
@@ -9997,14 +11216,27 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (DEBUG_FREEZE) Slog.v(TAG, "*** UNFREEZING DISPLAY", new RuntimeException());
-
mDisplayFrozen = false;
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
if (PROFILE_ORIENTATION) {
Debug.stopMethodTracing();
}
- Surface.unfreezeDisplay(0);
+
+ boolean updateRotation = false;
+
+ if (CUSTOM_SCREEN_ROTATION) {
+ if (mScreenRotationAnimation != null) {
+ if (mScreenRotationAnimation.dismiss(MAX_ANIMATION_DURATION,
+ mTransitionAnimationScale)) {
+ requestAnimationLocked(0);
+ } else {
+ mScreenRotationAnimation = null;
+ updateRotation = true;
+ }
+ }
+ } else {
+ Surface.unfreezeDisplay(0);
+ }
mInputMonitor.thawInputDispatchingLw();
@@ -10012,7 +11244,7 @@ public class WindowManagerService extends IWindowManager.Stub
// to avoid inconsistent states. However, something interesting
// could have actually changed during that time so re-evaluate it
// now to catch that.
- if (updateOrientationFromAppTokensLocked()) {
+ if (updateOrientationFromAppTokensLocked(false)) {
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
@@ -10026,6 +11258,15 @@ public class WindowManagerService extends IWindowManager.Stub
2000);
mScreenFrozenLock.release();
+
+ if (updateRotation) {
+ if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
+ boolean changed = setRotationUncheckedLocked(
+ WindowManagerPolicy.USE_LAST_ROTATION, 0, false);
+ if (changed) {
+ sendNewConfiguration();
+ }
+ }
}
static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps,
@@ -10047,7 +11288,7 @@ public class WindowManagerService extends IWindowManager.Stub
return val;
}
- class Watermark {
+ static class Watermark {
final String[] mTokens;
final String mText;
final Paint mTextPaint;
@@ -10063,9 +11304,9 @@ public class WindowManagerService extends IWindowManager.Stub
int mLastDH;
boolean mDrawNeeded;
- Watermark(SurfaceSession session, String[] tokens) {
+ Watermark(Display display, SurfaceSession session, String[] tokens) {
final DisplayMetrics dm = new DisplayMetrics();
- mDisplay.getMetrics(dm);
+ display.getMetrics(dm);
if (false) {
Log.i(TAG, "*********************** WATERMARK");
@@ -10159,6 +11400,8 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (OutOfResourcesException e) {
}
if (c != null) {
+ c.drawColor(0, PorterDuff.Mode.CLEAR);
+
int deltaX = mDeltaX;
int deltaY = mDeltaY;
@@ -10201,7 +11444,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (line != null) {
String[] toks = line.split("%");
if (toks != null && toks.length > 0) {
- mWatermark = new Watermark(mFxSession, toks);
+ mWatermark = new Watermark(mDisplay, mFxSession, toks);
}
}
} catch (FileNotFoundException e) {
@@ -10217,6 +11460,23 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void statusBarVisibilityChanged(int visibility) {
+ synchronized (mWindowMap) {
+ final int N = mWindows.size();
+ for (int i = 0; i < N; i++) {
+ WindowState ws = mWindows.get(i);
+ try {
+ if (ws.getAttrs().hasSystemUiListeners) {
+ ws.mClient.dispatchSystemUiVisibilityChanged(visibility);
+ }
+ } catch (RemoteException e) {
+ // so sorry
+ }
+ }
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
!= PackageManager.PERMISSION_GRANTED) {
@@ -10315,14 +11575,6 @@ public class WindowManagerService extends IWindowManager.Stub
token.dump(pw, " ");
}
}
- if (mTokenList.size() > 0) {
- pw.println(" ");
- pw.println(" Window token list:");
- for (int i=0; i<mTokenList.size(); i++) {
- pw.print(" #"); pw.print(i); pw.print(": ");
- pw.println(mTokenList.get(i));
- }
- }
if (mWallpaperTokens.size() > 0) {
pw.println(" ");
pw.println(" Wallpaper tokens:");
@@ -10382,6 +11634,9 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget);
pw.print(" mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget);
}
+ if (mWindowDetachedWallpaper != null) {
+ pw.print(" mWindowDetachedWallpaper="); pw.println(mWindowDetachedWallpaper);
+ }
pw.print(" mCurConfiguration="); pw.println(this.mCurConfiguration);
pw.print(" mInTouchMode="); pw.print(mInTouchMode);
pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
@@ -10407,6 +11662,8 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mRotation="); pw.print(mRotation);
pw.print(", mForcedAppOrientation="); pw.print(mForcedAppOrientation);
pw.print(", mRequestedRotation="); pw.println(mRequestedRotation);
+ pw.print(" mDeferredRotation="); pw.print(mDeferredRotation);
+ pw.print(", mDeferredRotationAnimFlags="); pw.print(mDeferredRotationAnimFlags);
pw.print(" mAnimationPending="); pw.print(mAnimationPending);
pw.print(" mWindowAnimationScale="); pw.print(mWindowAnimationScale);
pw.print(" mTransitionWindowAnimationScale="); pw.println(mTransitionAnimationScale);
@@ -10425,10 +11682,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition);
pw.print(", mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
- if (mLastEnterAnimToken != null || mLastEnterAnimToken != null) {
- pw.print(" mLastEnterAnimToken="); pw.print(mLastEnterAnimToken);
- pw.print(", mLastEnterAnimParams="); pw.println(mLastEnterAnimParams);
- }
if (mOpeningApps.size() > 0) {
pw.print(" mOpeningApps="); pw.println(mOpeningApps);
}
@@ -10441,8 +11694,14 @@ public class WindowManagerService extends IWindowManager.Stub
if (mToBottomApps.size() > 0) {
pw.print(" mToBottomApps="); pw.println(mToBottomApps);
}
- pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
- pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
+ if (mDisplay != null) {
+ pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
+ pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
+ } else {
+ pw.println(" NO DISPLAY");
+ }
+ pw.println(" Policy:");
+ mPolicy.dump(" ", fd, pw, args);
}
}
@@ -10510,7 +11769,7 @@ public class WindowManagerService extends IWindowManager.Stub
* Set's the dim surface's layer and update dim parameters that will be used in
* {@link updateSurface} after all windows are examined.
*/
- void updateParameters(WindowState w, long currentTime) {
+ void updateParameters(Resources res, WindowState w, long currentTime) {
mDimSurface.setLayer(w.mAnimLayer-1);
final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
@@ -10524,11 +11783,15 @@ public class WindowManagerService extends IWindowManager.Stub
? w.mAnimation.computeDurationHint()
: DEFAULT_DIM_DURATION;
if (target > mDimTargetAlpha) {
- // This is happening behind the activity UI,
- // so we can make it run a little longer to
- // give a stronger impression without disrupting
- // the user.
- duration *= DIM_DURATION_MULTIPLIER;
+ TypedValue tv = new TypedValue();
+ res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration,
+ tv, true);
+ if (tv.type == TypedValue.TYPE_FRACTION) {
+ duration = (long)tv.getFraction((float)duration, (float)duration);
+ } else if (tv.type >= TypedValue.TYPE_FIRST_INT
+ && tv.type <= TypedValue.TYPE_LAST_INT) {
+ duration = tv.data;
+ }
}
if (duration < 1) {
// Don't divide by zero
diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java
new file mode 100644
index 0000000..e45c368
--- /dev/null
+++ b/services/java/com/android/server/WiredAccessoryObserver.java
@@ -0,0 +1,299 @@
+/*
+ * 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;
+
+import android.app.ActivityManagerNative;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.UEventObserver;
+import android.util.Slog;
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.io.FileReader;
+import java.io.FileNotFoundException;
+
+/**
+ * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock.
+ */
+class WiredAccessoryObserver extends UEventObserver {
+ private static final String TAG = WiredAccessoryObserver.class.getSimpleName();
+ private static final boolean LOG = true;
+ private static final int MAX_AUDIO_PORTS = 3; /* h2w, USB Audio & hdmi */
+ private static final String uEventInfo[][] = { {"DEVPATH=/devices/virtual/switch/h2w",
+ "/sys/class/switch/h2w/state",
+ "/sys/class/switch/h2w/name"},
+ {"DEVPATH=/devices/virtual/switch/usb_audio",
+ "/sys/class/switch/usb_audio/state",
+ "/sys/class/switch/usb_audio/name"},
+ {"DEVPATH=/devices/virtual/switch/hdmi",
+ "/sys/class/switch/hdmi/state",
+ "/sys/class/switch/hdmi/name"} };
+
+ private static final int BIT_HEADSET = (1 << 0);
+ private static final int BIT_HEADSET_NO_MIC = (1 << 1);
+ private static final int BIT_USB_HEADSET_ANLG = (1 << 2);
+ private static final int BIT_USB_HEADSET_DGTL = (1 << 3);
+ private static final int BIT_HDMI_AUDIO = (1 << 4);
+ private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+ BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL|
+ BIT_HDMI_AUDIO);
+ private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
+
+ private int mHeadsetState;
+ private int mPrevHeadsetState;
+ private String mHeadsetName;
+ private int switchState;
+
+ private final Context mContext;
+ private final WakeLock mWakeLock; // held while there is a pending route change
+
+ public WiredAccessoryObserver(Context context) {
+ mContext = context;
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver");
+ mWakeLock.setReferenceCounted(false);
+
+ context.registerReceiver(new BootCompletedReceiver(),
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+ }
+
+ private final class BootCompletedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // At any given time accessories could be inserted
+ // one on the board, one on the dock and one on HDMI:
+ // observe three UEVENTs
+ init(); // set initial status
+ for (int i = 0; i < MAX_AUDIO_PORTS; i++) {
+ startObserving(uEventInfo[i][0]);
+ }
+ }
+ }
+
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
+
+ try {
+ String name = event.get("SWITCH_NAME");
+ int state = Integer.parseInt(event.get("SWITCH_STATE"));
+ updateState(name, state);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Could not parse switch state from event " + event);
+ }
+ }
+
+ private synchronized final void updateState(String name, int state)
+ {
+ if (name.equals("usb_audio")) {
+ if (state == 1) {
+ switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+ BIT_USB_HEADSET_DGTL|BIT_HDMI_AUDIO)) |
+ (state << 2));
+ } else if (state == 2) {
+ switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+ BIT_USB_HEADSET_ANLG|BIT_HDMI_AUDIO)) |
+ (state << 3));
+ } else switchState = (mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|BIT_HDMI_AUDIO));
+ }
+ else if (name.equals("hdmi")) {
+ switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+ BIT_USB_HEADSET_DGTL|BIT_USB_HEADSET_ANLG)) |
+ (state << 4));
+ }
+ else {
+ switchState = ((mHeadsetState & (BIT_HDMI_AUDIO|BIT_USB_HEADSET_ANLG|
+ BIT_USB_HEADSET_DGTL)) |
+ state);
+ }
+ update(name, switchState);
+ }
+
+ private synchronized final void init() {
+ char[] buffer = new char[1024];
+
+ String newName = mHeadsetName;
+ int newState = mHeadsetState;
+ mPrevHeadsetState = mHeadsetState;
+
+ if (LOG) Slog.v(TAG, "init()");
+
+ for (int i = 0; i < MAX_AUDIO_PORTS; i++) {
+ try {
+ FileReader file = new FileReader(uEventInfo[i][1]);
+ int len = file.read(buffer, 0, 1024);
+ file.close();
+ newState = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ file = new FileReader(uEventInfo[i][2]);
+ len = file.read(buffer, 0, 1024);
+ file.close();
+ newName = new String(buffer, 0, len).trim();
+
+ if (newState > 0) {
+ updateState(newName, newState);
+ }
+
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "This kernel does not have wired headset support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+ }
+ }
+
+ private synchronized final void update(String newName, int newState) {
+ // Retain only relevant bits
+ int headsetState = newState & SUPPORTED_HEADSETS;
+ int newOrOld = headsetState | mHeadsetState;
+ int delay = 0;
+ int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
+ int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
+ int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC);
+ boolean h2wStateChange = true;
+ boolean usbStateChange = true;
+ // reject all suspect transitions: only accept state changes from:
+ // - a: 0 heaset to 1 headset
+ // - b: 1 headset to 0 headset
+ if (LOG) Slog.v(TAG, "newState = "+newState+", headsetState = "+headsetState+","
+ + "mHeadsetState = "+mHeadsetState);
+ if (mHeadsetState == headsetState || ((h2w_headset & (h2w_headset - 1)) != 0)) {
+ Log.e(TAG, "unsetting h2w flag");
+ h2wStateChange = false;
+ }
+ // - c: 0 usb headset to 1 usb headset
+ // - d: 1 usb headset to 0 usb headset
+ if ((usb_headset_anlg >> 2) == 1 && (usb_headset_dgtl >> 3) == 1) {
+ Log.e(TAG, "unsetting usb flag");
+ usbStateChange = false;
+ }
+ if (!h2wStateChange && !usbStateChange) {
+ Log.e(TAG, "invalid transition, returning ...");
+ return;
+ }
+
+ mHeadsetName = newName;
+ mPrevHeadsetState = mHeadsetState;
+ mHeadsetState = headsetState;
+
+ if (headsetState == 0) {
+ Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+ mContext.sendBroadcast(intent);
+ // It can take hundreds of ms flush the audio pipeline after
+ // apps pause audio playback, but audio route changes are
+ // immediate, so delay the route change by 1000ms.
+ // This could be improved once the audio sub-system provides an
+ // interface to clear the audio pipeline.
+ delay = 1000;
+ } else {
+ // Insert the same delay for headset connection so that the connection event is not
+ // broadcast before the disconnection event in case of fast removal/insertion
+ if (mHandler.hasMessages(0)) {
+ delay = 1000;
+ }
+ }
+ mWakeLock.acquire();
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
+ mHeadsetState,
+ mPrevHeadsetState,
+ mHeadsetName),
+ delay);
+ }
+
+ private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) {
+ int allHeadsets = SUPPORTED_HEADSETS;
+ for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
+ if ((curHeadset & allHeadsets) != 0) {
+ sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName);
+ allHeadsets &= ~curHeadset;
+ }
+ }
+ }
+
+ private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
+ if ((headsetState & headset) != (prevHeadsetState & headset)) {
+
+ int state = 0;
+ if ((headsetState & headset) != 0) {
+ state = 1;
+ }
+ if((headset == BIT_USB_HEADSET_ANLG) || (headset == BIT_USB_HEADSET_DGTL) ||
+ (headset == BIT_HDMI_AUDIO)) {
+ Intent intent;
+
+ // Pack up the values and broadcast them to everyone
+ if (headset == BIT_USB_HEADSET_ANLG) {
+ intent = new Intent(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra("state", state);
+ intent.putExtra("name", headsetName);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ } else if (headset == BIT_USB_HEADSET_DGTL) {
+ intent = new Intent(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra("state", state);
+ intent.putExtra("name", headsetName);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ } else if (headset == BIT_HDMI_AUDIO) {
+ intent = new Intent(Intent.ACTION_HDMI_AUDIO_PLUG);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra("state", state);
+ intent.putExtra("name", headsetName);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ }
+
+ if (LOG) Slog.v(TAG, "Intent.ACTION_USB_HEADSET_PLUG: state: "+state+" name: "+headsetName);
+ // TODO: Should we require a permission?
+ }
+ if((headset == BIT_HEADSET) || (headset == BIT_HEADSET_NO_MIC)) {
+
+ // Pack up the values and broadcast them to everyone
+ Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ //int state = 0;
+ int microphone = 0;
+
+ if ((headset & HEADSETS_WITH_MIC) != 0) {
+ microphone = 1;
+ }
+
+ intent.putExtra("state", state);
+ intent.putExtra("name", headsetName);
+ intent.putExtra("microphone", microphone);
+
+ if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
+ // TODO: Should we require a permission?
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ }
+ }
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ sendIntents(msg.arg1, msg.arg2, (String)msg.obj);
+ mWakeLock.release();
+ }
+ };
+}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ddd99f4..6bb19b0 100755
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -55,6 +55,7 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IIntentReceiver;
@@ -75,6 +76,8 @@ import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.net.Proxy;
+import android.net.ProxyProperties;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -553,7 +556,7 @@ public final class ActivityManagerService extends ActivityManagerNative
= new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
/**
- * Fingerprints (String.hashCode()) of stack traces that we've
+ * Fingerprints (hashCode()) of stack traces that we've
* already logged DropBox entries for. Guarded by itself. If
* something (rogue user app) forces this over
* MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared.
@@ -958,6 +961,8 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
+ static final int CLEAR_DNS_CACHE = 28;
+ static final int UPDATE_HTTP_PROXY = 29;
AlertDialog mUidAlert;
@@ -1109,6 +1114,44 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
} break;
+ case CLEAR_DNS_CACHE: {
+ synchronized (ActivityManagerService.this) {
+ for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = mLruProcesses.get(i);
+ if (r.thread != null) {
+ try {
+ r.thread.clearDnsCache();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
+ }
+ }
+ }
+ }
+ } break;
+ case UPDATE_HTTP_PROXY: {
+ ProxyProperties proxy = (ProxyProperties)msg.obj;
+ String host = "";
+ String port = "";
+ String exclList = "";
+ if (proxy != null) {
+ host = proxy.getHost();
+ port = Integer.toString(proxy.getPort());
+ exclList = proxy.getExclusionList();
+ }
+ synchronized (ActivityManagerService.this) {
+ for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = mLruProcesses.get(i);
+ if (r.thread != null) {
+ try {
+ r.thread.setHttpProxy(host, port, exclList);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to update http proxy for: " +
+ r.info.processName);
+ }
+ }
+ }
+ }
+ } break;
case SHOW_UID_ERROR_MSG: {
// XXX This is a temporary dialog, no need to localize.
AlertDialog d = new BaseErrorDialog(mContext);
@@ -1116,7 +1159,7 @@ public final class ActivityManagerService extends ActivityManagerNative
d.setCancelable(false);
d.setTitle("System UIDs Inconsistent");
d.setMessage("UIDs on the system are inconsistent, you need to wipe your data partition or your device will be unstable.");
- d.setButton("I'm Feeling Lucky",
+ d.setButton(DialogInterface.BUTTON_POSITIVE, "I'm Feeling Lucky",
mHandler.obtainMessage(IM_FEELING_LUCKY_MSG));
mUidAlert = d;
d.show();
@@ -1281,6 +1324,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ActivityThread at = ActivityThread.systemMain();
mSystemThread = at;
Context context = at.getSystemContext();
+ context.setTheme(android.R.style.Theme_Holo);
m.mContext = context;
m.mFactoryTest = factoryTest;
m.mMainStack = new ActivityStack(m, context, true);
@@ -1333,6 +1377,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode logging for AThread's Looper");
+ }
+
Looper.loop();
}
}
@@ -1977,7 +2026,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
- null, null, 0, 0, 0, false, false);
+ null, null, 0, 0, 0, false, false, null);
}
}
@@ -2033,7 +2082,7 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
mMainStack.startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
- null, null, 0, 0, 0, false, false);
+ null, null, 0, 0, 0, false, false, null);
}
}
}
@@ -2072,13 +2121,13 @@ public final class ActivityManagerService extends ActivityManagerNative
}
mPendingActivityLaunches.clear();
}
-
+
public final int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
- return mMainStack.startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, null);
}
@@ -2089,7 +2138,7 @@ public final class ActivityManagerService extends ActivityManagerNative
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
WaitResult res = new WaitResult();
- mMainStack.startActivityMayWait(caller, intent, resolvedType,
+ mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, res, null);
return res;
@@ -2100,12 +2149,12 @@ public final class ActivityManagerService extends ActivityManagerNative
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, Configuration config) {
- return mMainStack.startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, config);
}
- public int startActivityIntentSender(IApplicationThread caller,
+ public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues) {
@@ -2218,7 +2267,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// those are not yet exposed to user code, so there is no need.
int res = mMainStack.startActivityLocked(r.app.thread, intent,
r.resolvedType, null, 0, aInfo, resultTo, resultWho,
- requestCode, -1, r.launchedFromUid, false, false);
+ requestCode, -1, r.launchedFromUid, false, false, null);
Binder.restoreCallingIdentity(origId);
r.finishing = wasFinishing;
@@ -2240,43 +2289,37 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(
"startActivityInPackage only available to the system");
}
-
- final boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
- // Collect information about the target of the Intent.
- ActivityInfo aInfo;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
- }
+ return mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
+ null, 0, resultTo, resultWho, requestCode, onlyIfNeeded, false, null, null);
+ }
- if (aInfo != null) {
- // Store the found target back into the intent, because now that
- // we have it we never want to do this again. For example, if the
- // user navigates back to this point in the history, we should
- // always restart the exact same activity.
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
- }
+ public final int startActivities(IApplicationThread caller,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+ return mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo);
+ }
- synchronized(this) {
- return mMainStack.startActivityLocked(null, intent, resolvedType,
- null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid,
- onlyIfNeeded, componentSpecified);
+ public final int startActivitiesInPackage(int uid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+
+ // This is so super not safe, that only the system (or okay root)
+ // can do it.
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.myUid()) {
+ throw new SecurityException(
+ "startActivityInPackage only available to the system");
}
+
+ return mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo);
}
final void addRecentTaskLocked(TaskRecord task) {
- // Remove any existing entries that are the same kind of task.
int N = mRecentTasks.size();
+ // Quick case: check if the top-most recent task is the same.
+ if (N > 0 && mRecentTasks.get(0) == task) {
+ return;
+ }
+ // Remove any existing entries that are the same kind of task.
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if ((task.affinity != null && task.affinity.equals(tr.affinity))
@@ -2450,6 +2493,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (proc.thread != null) {
+ if (proc.pid == Process.myPid()) {
+ Log.w(TAG, "crashApplication: trying to crash self!");
+ return;
+ }
long ident = Binder.clearCallingIdentity();
try {
proc.thread.scheduleCrash(message);
@@ -2565,6 +2612,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (localLOGV) Slog.v(
TAG, "Removing this entry! frozen=" + r.haveState
+ " finishing=" + r.finishing);
+ r.makeFinishing();
mMainStack.mHistory.remove(i);
r.inHistory = false;
@@ -3012,7 +3060,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (uid == pkgUid || checkComponentPermission(
android.Manifest.permission.CLEAR_APP_USER_DATA,
- pid, uid, -1)
+ pid, uid, -1, true)
== PackageManager.PERMISSION_GRANTED) {
forceStopPackageLocked(packageName, pkgUid);
} else {
@@ -3678,10 +3726,12 @@ public final class ActivityManagerService extends ActivityManagerNative
String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
if (pkgs != null) {
for (String pkg : pkgs) {
- if (forceStopPackageLocked(pkg, -1, false, false, false)) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
+ synchronized (ActivityManagerService.this) {
+ if (forceStopPackageLocked(pkg, -1, false, false, false)) {
+ setResultCode(Activity.RESULT_OK);
+ return;
+ }
+ }
}
}
}
@@ -3735,22 +3785,22 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public final void activityPaused(IBinder token, Bundle icicle) {
- // Refuse possible leaked file descriptors
- if (icicle != null && icicle.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Bundle");
- }
-
+ public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
- mMainStack.activityPaused(token, icicle, false);
+ mMainStack.activityPaused(token, false);
Binder.restoreCallingIdentity(origId);
}
- public final void activityStopped(IBinder token, Bitmap thumbnail,
+ public final void activityStopped(IBinder token, Bundle icicle, Bitmap thumbnail,
CharSequence description) {
if (localLOGV) Slog.v(
TAG, "Activity stopped: token=" + token);
+ // Refuse possible leaked file descriptors
+ if (icicle != null && icicle.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Bundle");
+ }
+
ActivityRecord r = null;
final long origId = Binder.clearCallingIdentity();
@@ -3759,8 +3809,18 @@ public final class ActivityManagerService extends ActivityManagerNative
int index = mMainStack.indexOfTokenLocked(token);
if (index >= 0) {
r = (ActivityRecord)mMainStack.mHistory.get(index);
- r.thumbnail = thumbnail;
+ r.icicle = icicle;
+ r.haveState = true;
+ if (thumbnail != null) {
+ r.thumbnail = thumbnail;
+ if (r.task != null) {
+ r.task.lastThumbnail = r.thumbnail;
+ }
+ }
r.description = description;
+ if (r.task != null) {
+ r.task.lastDescription = r.description;
+ }
r.stopped = true;
r.state = ActivityState.STOPPED;
if (!r.finishing) {
@@ -3835,16 +3895,30 @@ public final class ActivityManagerService extends ActivityManagerNative
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags) {
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
// Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors() == true) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- if (type == INTENT_SENDER_BROADCAST) {
- if ((intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ if (intents != null) {
+ if (intents.length < 1) {
+ throw new IllegalArgumentException("Intents array length must be >= 1");
+ }
+ for (int i=0; i<intents.length; i++) {
+ Intent intent = intents[i];
+ if (intent == null) {
+ throw new IllegalArgumentException("Null intent at index " + i);
+ }
+ if (intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ if (type == INTENT_SENDER_BROADCAST &&
+ (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ throw new IllegalArgumentException(
+ "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ }
+ intents[i] = new Intent(intent);
+ }
+ if (resolvedTypes != null && resolvedTypes.length != intents.length) {
throw new IllegalArgumentException(
- "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ "Intent array length does not match resolvedTypes length");
}
}
@@ -3867,7 +3941,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
return getIntentSenderLocked(type, packageName, callingUid,
- token, resultWho, requestCode, intent, resolvedType, flags);
+ token, resultWho, requestCode, intents, resolvedTypes, flags);
} catch (RemoteException e) {
throw new SecurityException(e);
@@ -3877,7 +3951,7 @@ public final class ActivityManagerService extends ActivityManagerNative
IIntentSender getIntentSenderLocked(int type,
String packageName, int callingUid, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags) {
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
ActivityRecord activity = null;
if (type == INTENT_SENDER_ACTIVITY_RESULT) {
int index = mMainStack.indexOfTokenLocked(token);
@@ -3898,14 +3972,24 @@ public final class ActivityManagerService extends ActivityManagerNative
PendingIntentRecord.Key key = new PendingIntentRecord.Key(
type, packageName, activity, resultWho,
- requestCode, intent, resolvedType, flags);
+ requestCode, intents, resolvedTypes, flags);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
if (rec != null) {
if (!cancelCurrent) {
if (updateCurrent) {
- rec.key.requestIntent.replaceExtras(intent);
+ if (rec.key.requestIntent != null) {
+ rec.key.requestIntent.replaceExtras(intents != null ? intents[0] : null);
+ }
+ if (intents != null) {
+ intents[intents.length-1] = rec.key.requestIntent;
+ rec.key.allIntents = intents;
+ rec.key.allResolvedTypes = resolvedTypes;
+ } else {
+ rec.key.allIntents = null;
+ rec.key.allResolvedTypes = null;
+ }
}
return rec;
}
@@ -4067,7 +4151,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* This can be called with or without the global lock held.
*/
int checkComponentPermission(String permission, int pid, int uid,
- int reqUid) {
+ int owningUid, boolean exported) {
// We might be performing an operation on behalf of an indirect binder
// invocation, e.g. via {@link #openContentUri}. Check and adjust the
// client identity accordingly before proceeding.
@@ -4084,9 +4168,14 @@ public final class ActivityManagerService extends ActivityManagerNative
!Process.supportsProcesses()) {
return PackageManager.PERMISSION_GRANTED;
}
- // If the target requires a specific UID, always fail for others.
- if (reqUid >= 0 && uid != reqUid) {
- Slog.w(TAG, "Permission denied: checkComponentPermission() reqUid=" + reqUid);
+ // If there is a uid that owns whatever is being accessed, it has
+ // blanket access to it regardless of the permissions it requires.
+ if (owningUid >= 0 && uid == owningUid) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ // If the target is not exported, then nobody else can get to it.
+ if (!exported) {
+ Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
return PackageManager.PERMISSION_DENIED;
}
if (permission == null) {
@@ -4115,7 +4204,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkComponentPermission(permission, pid, uid, -1);
+ return checkComponentPermission(permission, pid, uid, -1, true);
}
/**
@@ -4265,8 +4354,10 @@ public final class ActivityManagerService extends ActivityManagerNative
return -1;
}
- if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Checking grant " + targetPkg + " permission to " + uri);
+ if (targetPkg != null) {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+ "Checking grant " + targetPkg + " permission to " + uri);
+ }
final IPackageManager pm = AppGlobals.getPackageManager();
@@ -4295,23 +4386,45 @@ public final class ActivityManagerService extends ActivityManagerNative
}
int targetUid;
- try {
- targetUid = pm.getPackageUid(targetPkg);
- if (targetUid < 0) {
- if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Can't grant URI permission no uid for: " + targetPkg);
+ if (targetPkg != null) {
+ try {
+ targetUid = pm.getPackageUid(targetPkg);
+ if (targetUid < 0) {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+ "Can't grant URI permission no uid for: " + targetPkg);
+ return -1;
+ }
+ } catch (RemoteException ex) {
return -1;
}
- } catch (RemoteException ex) {
- return -1;
+ } else {
+ targetUid = -1;
}
- // First... does the target actually need this permission?
- if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
- // No need to grant the target this permission.
- if (DEBUG_URI_PERMISSION) Slog.v(TAG,
- "Target " + targetPkg + " already has full permission to " + uri);
- return -1;
+ if (targetUid >= 0) {
+ // First... does the target actually need this permission?
+ if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
+ // No need to grant the target this permission.
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+ "Target " + targetPkg + " already has full permission to " + uri);
+ return -1;
+ }
+ } else {
+ // First... there is no target package, so can anyone access it?
+ boolean allowed = pi.exported;
+ if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ if (pi.readPermission != null) {
+ allowed = false;
+ }
+ }
+ if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ if (pi.writePermission != null) {
+ allowed = false;
+ }
+ }
+ if (allowed) {
+ return -1;
+ }
}
// Second... is the provider allowing granting of URI permissions?
@@ -4341,16 +4454,25 @@ public final class ActivityManagerService extends ActivityManagerNative
// Third... does the caller itself have permission to access
// this uri?
- if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
- if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
- throw new SecurityException("Uid " + callingUid
- + " does not have permission to uri " + uri);
+ if (callingUid != Process.myUid()) {
+ if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
+ if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
+ throw new SecurityException("Uid " + callingUid
+ + " does not have permission to uri " + uri);
+ }
}
}
return targetUid;
}
+ public int checkGrantUriPermission(int callingUid, String targetPkg,
+ Uri uri, int modeFlags) {
+ synchronized(this) {
+ return checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
+ }
+ }
+
void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg,
Uri uri, int modeFlags, UriPermissionOwner owner) {
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -4393,6 +4515,10 @@ public final class ActivityManagerService extends ActivityManagerNative
void grantUriPermissionLocked(int callingUid,
String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
+ if (targetPkg == null) {
+ throw new NullPointerException("targetPkg");
+ }
+
int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
if (targetUid < 0) {
return;
@@ -4411,6 +4537,10 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " from " + intent + "; flags=0x"
+ Integer.toHexString(intent != null ? intent.getFlags() : 0));
+ if (targetPkg == null) {
+ throw new NullPointerException("targetPkg");
+ }
+
if (intent == null) {
return -1;
}
@@ -4712,6 +4842,11 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
+ final boolean canReadFb = (flags&ActivityManager.TASKS_GET_THUMBNAILS) != 0
+ && checkCallingPermission(
+ android.Manifest.permission.READ_FRAME_BUFFER)
+ == PackageManager.PERMISSION_GRANTED;
+
int pos = mMainStack.mHistory.size()-1;
ActivityRecord next =
pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
@@ -4756,7 +4891,13 @@ public final class ActivityManagerService extends ActivityManagerNative
ci.id = curTask.taskId;
ci.baseActivity = r.intent.getComponent();
ci.topActivity = top.intent.getComponent();
- ci.thumbnail = top.thumbnail;
+ if (canReadFb) {
+ if (top.thumbnail != null) {
+ ci.thumbnail = top.thumbnail;
+ } else if (top.state == ActivityState.RESUMED) {
+ ci.thumbnail = top.stack.screenshotActivities(top);
+ }
+ }
ci.description = topDescription;
ci.numActivities = numActivities;
ci.numRunning = numRunning;
@@ -4825,8 +4966,15 @@ public final class ActivityManagerService extends ActivityManagerNative
enforceCallingPermission(android.Manifest.permission.GET_TASKS,
"getRecentTasks()");
+ final boolean canReadFb = (flags&ActivityManager.TASKS_GET_THUMBNAILS) != 0
+ && checkCallingPermission(
+ android.Manifest.permission.READ_FRAME_BUFFER)
+ == PackageManager.PERMISSION_GRANTED;
+
IPackageManager pm = AppGlobals.getPackageManager();
+ ActivityRecord resumed = mMainStack.mResumedActivity;
+
final int N = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res
= new ArrayList<ActivityManager.RecentTaskInfo>(
@@ -4844,6 +4992,15 @@ public final class ActivityManagerService extends ActivityManagerNative
tr.intent != null ? tr.intent : tr.affinityIntent);
rti.origActivity = tr.origActivity;
+ if (canReadFb) {
+ if (resumed != null && resumed.task == tr) {
+ rti.thumbnail = resumed.stack.screenshotActivities(resumed);
+ } else {
+ rti.thumbnail = tr.lastThumbnail;
+ }
+ }
+ rti.description = tr.lastDescription;
+
if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) {
// Check whether this activity is currently available.
try {
@@ -4910,7 +5067,7 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* TODO: Add mController hook
*/
- public void moveTaskToFront(int task) {
+ public void moveTaskToFront(int task, int flags) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskToFront()");
@@ -4925,6 +5082,11 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if (tr.taskId == task) {
+ if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+ // Caller wants the home activity moved with it. To accomplish this,
+ // we'll just move the home task to the top first.
+ mMainStack.moveHomeToFrontLocked();
+ }
mMainStack.moveTaskToFrontLocked(tr, null);
return;
}
@@ -4932,6 +5094,11 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
if (hr.task.taskId == task) {
+ if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+ // Caller wants the home activity moved with it. To accomplish this,
+ // we'll just move the home task to the top first.
+ mMainStack.moveHomeToFrontLocked();
+ }
mMainStack.moveTaskToFrontLocked(hr.task, null);
return;
}
@@ -5160,12 +5327,12 @@ public final class ActivityManagerService extends ActivityManagerNative
final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid();
if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
- cpi.exported ? -1 : cpi.applicationInfo.uid)
+ cpi.applicationInfo.uid, cpi.exported)
== PackageManager.PERMISSION_GRANTED) {
return null;
}
if (checkComponentPermission(cpi.writePermission, callingPid, callingUid,
- cpi.exported ? -1 : cpi.applicationInfo.uid)
+ cpi.applicationInfo.uid, cpi.exported)
== PackageManager.PERMISSION_GRANTED) {
return null;
}
@@ -5177,12 +5344,12 @@ public final class ActivityManagerService extends ActivityManagerNative
i--;
PathPermission pp = pps[i];
if (checkComponentPermission(pp.getReadPermission(), callingPid, callingUid,
- cpi.exported ? -1 : cpi.applicationInfo.uid)
+ cpi.applicationInfo.uid, cpi.exported)
== PackageManager.PERMISSION_GRANTED) {
return null;
}
if (checkComponentPermission(pp.getWritePermission(), callingPid, callingUid,
- cpi.exported ? -1 : cpi.applicationInfo.uid)
+ cpi.applicationInfo.uid, cpi.exported)
== PackageManager.PERMISSION_GRANTED) {
return null;
}
@@ -5198,10 +5365,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- String msg = "Permission Denial: opening provider " + cpi.name
- + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
- + ", uid=" + callingUid + ") requires "
- + cpi.readPermission + " or " + cpi.writePermission;
+ String msg;
+ if (!cpi.exported) {
+ msg = "Permission Denial: opening provider " + cpi.name
+ + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") that is not exported from uid "
+ + cpi.applicationInfo.uid;
+ } else {
+ msg = "Permission Denial: opening provider " + cpi.name
+ + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") requires "
+ + cpi.readPermission + " or " + cpi.writePermission;
+ }
Slog.w(TAG, msg);
return msg;
}
@@ -5683,16 +5858,18 @@ public final class ActivityManagerService extends ActivityManagerNative
return pfd;
}
+ // Actually is sleeping or shutting down or whatever else in the future
+ // is an inactive state.
+ public boolean isSleeping() {
+ return mSleeping || mShuttingDown;
+ }
+
public void goingToSleep() {
synchronized(this) {
mSleeping = true;
mWindowManager.setEventDispatching(false);
- if (mMainStack.mResumedActivity != null) {
- mMainStack.pauseIfSleepingLocked();
- } else {
- Slog.w(TAG, "goingToSleep with no resumed activity!");
- }
+ mMainStack.stopIfSleepingLocked();
// Initialize the wake times of all processes.
checkExcessivePowerUsageLocked(false);
@@ -5716,7 +5893,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mWindowManager.setEventDispatching(false);
if (mMainStack.mResumedActivity != null) {
- mMainStack.pauseIfSleepingLocked();
+ mMainStack.stopIfSleepingLocked();
final long endTime = System.currentTimeMillis() + timeout;
while (mMainStack.mResumedActivity != null
|| mMainStack.mPausingActivity != null) {
@@ -5740,13 +5917,30 @@ public final class ActivityManagerService extends ActivityManagerNative
return timedout;
}
+ public final void activitySlept(IBinder token) {
+ if (localLOGV) Slog.v(
+ TAG, "Activity slept: token=" + token);
+
+ ActivityRecord r = null;
+
+ final long origId = Binder.clearCallingIdentity();
+
+ synchronized (this) {
+ int index = mMainStack.indexOfTokenLocked(token);
+ if (index >= 0) {
+ r = (ActivityRecord)mMainStack.mHistory.get(index);
+ mMainStack.activitySleptLocked(r);
+ }
+ }
+
+ Binder.restoreCallingIdentity(origId);
+ }
+
public void wakingUp() {
synchronized(this) {
- if (mMainStack.mGoingToSleep.isHeld()) {
- mMainStack.mGoingToSleep.release();
- }
mWindowManager.setEventDispatching(true);
mSleeping = false;
+ mMainStack.awakeFromSleepingLocked();
mMainStack.resumeTopActivityLocked(null);
}
}
@@ -5791,7 +5985,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final int perm = checkComponentPermission(
android.Manifest.permission.STOP_APP_SWITCHES, callingPid,
- callingUid, -1);
+ callingUid, -1, true);
if (perm == PackageManager.PERMISSION_GRANTED) {
return true;
}
@@ -5875,6 +6069,35 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ public void setImmersive(IBinder token, boolean immersive) {
+ synchronized(this) {
+ int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
+ if (index < 0) {
+ throw new IllegalArgumentException();
+ }
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
+ r.immersive = immersive;
+ }
+ }
+
+ public boolean isImmersive(IBinder token) {
+ synchronized (this) {
+ int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
+ if (index < 0) {
+ throw new IllegalArgumentException();
+ }
+ ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
+ return r.immersive;
+ }
+ }
+
+ public boolean isTopActivityImmersive() {
+ synchronized (this) {
+ ActivityRecord r = mMainStack.topRunningActivityLocked(null);
+ return (r != null) ? r.immersive : false;
+ }
+ }
+
public final void enterSafeMode() {
synchronized(this) {
// It only makes sense to do this before the system is ready
@@ -5884,23 +6107,25 @@ public final class ActivityManagerService extends ActivityManagerNative
AppGlobals.getPackageManager().enterSafeMode();
} catch (RemoteException e) {
}
-
- View v = LayoutInflater.from(mContext).inflate(
- com.android.internal.R.layout.safe_mode, null);
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
- lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
- lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
- lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
- lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
- lp.format = v.getBackground().getOpacity();
- lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- ((WindowManager)mContext.getSystemService(
- Context.WINDOW_SERVICE)).addView(v, lp);
}
}
}
+ public final void showSafeModeOverlay() {
+ View v = LayoutInflater.from(mContext).inflate(
+ com.android.internal.R.layout.safe_mode, null);
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
+ lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+ lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
+ lp.format = v.getBackground().getOpacity();
+ lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ ((WindowManager)mContext.getSystemService(
+ Context.WINDOW_SERVICE)).addView(v, lp);
+ }
+
public void noteWakeupAlarm(IIntentSender sender) {
if (!(sender instanceof PendingIntentRecord)) {
return;
@@ -6372,7 +6597,6 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " has crashed too many times: killing!");
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.info.processName, app.info.uid);
- killServicesLocked(app, false);
for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
if (r.app == app) {
@@ -6382,6 +6606,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
if (!app.persistent) {
+ // Don't let services in this process be restarted and potentially
+ // annoy the user repeatedly. Unless it is persistent, since those
+ // processes run critical code.
+ killServicesLocked(app, false);
// We don't want to start this process again until the user
// explicitly does so... but for persistent process, we really
// need to keep it running. If a persistent process is actually
@@ -6405,7 +6633,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int index = mMainStack.indexOfTokenLocked(r);
r.stack.finishActivityLocked(r, index,
Activity.RESULT_CANCELED, null, "crashed");
- // Also terminate an activities below it that aren't yet
+ // Also terminate any activities below it that aren't yet
// stopped, to avoid a situation where one will get
// re-start our crashing activity once it gets resumed again.
index--;
@@ -6414,7 +6642,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (r.state == ActivityState.RESUMED
|| r.state == ActivityState.PAUSING
|| r.state == ActivityState.PAUSED) {
- if (!r.isHomeActivity) {
+ if (!r.isHomeActivity || mHomeProcess != r.app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
r.stack.finishActivityLocked(r, index,
@@ -6435,7 +6663,28 @@ public final class ActivityManagerService extends ActivityManagerNative
sr.crashCount++;
}
}
-
+
+ // If the crashing process is what we consider to be the "home process" and it has been
+ // replaced by a third-party app, clear the package preferred activities from packages
+ // with a home activity running in the process to prevent a repeatedly crashing app
+ // from blocking the user to manually clear the list.
+ if (app == mHomeProcess && mHomeProcess.activities.size() > 0
+ && (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Iterator it = mHomeProcess.activities.iterator();
+ while (it.hasNext()) {
+ ActivityRecord r = (ActivityRecord)it.next();
+ if (r.isHomeActivity) {
+ Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
+ try {
+ ActivityThread.getPackageManager()
+ .clearPackagePreferredActivities(r.packageName);
+ } catch (RemoteException c) {
+ // pm is in same process, this will never happen.
+ }
+ }
+ }
+ }
+
mProcessCrashTimes.put(app.info.processName, app.info.uid, now);
return true;
}
@@ -6501,7 +6750,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ProcessRecord r = findAppProcess(app);
if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
- Integer stackFingerprint = info.crashInfo.stackTrace.hashCode();
+ Integer stackFingerprint = info.hashCode();
boolean logIt = true;
synchronized (mAlreadyLoggedViolatedStacks) {
if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
@@ -6578,9 +6827,23 @@ public final class ActivityManagerService extends ActivityManagerNative
if (info.violationNumThisLoop != 0) {
sb.append("Loop-Violation-Number: ").append(info.violationNumThisLoop).append("\n");
}
- if (info != null && info.durationMillis != -1) {
+ if (info.numAnimationsRunning != 0) {
+ sb.append("Animations-Running: ").append(info.numAnimationsRunning).append("\n");
+ }
+ if (info.broadcastIntentAction != null) {
+ sb.append("Broadcast-Intent-Action: ").append(info.broadcastIntentAction).append("\n");
+ }
+ if (info.durationMillis != -1) {
sb.append("Duration-Millis: ").append(info.durationMillis).append("\n");
}
+ if (info.numInstances != -1) {
+ sb.append("Instance-Count: ").append(info.numInstances).append("\n");
+ }
+ if (info.tags != null) {
+ for (String tag : info.tags) {
+ sb.append("Span-Tag: ").append(tag).append("\n");
+ }
+ }
sb.append("\n");
if (info.crashInfo != null && info.crashInfo.stackTrace != null) {
sb.append(info.crashInfo.stackTrace);
@@ -6789,6 +7052,9 @@ public final class ActivityManagerService extends ActivityManagerNative
sb.append("Subject: ").append(subject).append("\n");
}
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
+ if (Debug.isDebuggerConnected()) {
+ sb.append("Debugger: Connected\n");
+ }
sb.append("\n");
// Do the rest in a worker thread to avoid blocking the caller on I/O
@@ -7142,6 +7408,9 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(" prov[iders]: content provider state");
pw.println(" s[ervices]: service state");
pw.println(" service [name]: service client-side state");
+ pw.println(" cmd may also be a component name (com.foo/.myApp),");
+ pw.println(" a partial substring in a component name, or an");
+ pw.println(" ActivityRecord hex object identifier.");
return;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
@@ -7183,13 +7452,21 @@ public final class ActivityManagerService extends ActivityManagerNative
}
return;
} else if ("service".equals(cmd)) {
- dumpService(fd, pw, args, opti, dumpAll);
+ dumpService(fd, pw, args, opti);
return;
} else if ("services".equals(cmd) || "s".equals(cmd)) {
synchronized (this) {
dumpServicesLocked(fd, pw, args, opti, true);
}
return;
+ } else {
+ // Dumping a single activity?
+ if (dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
+ return;
+ }
+ pw.println("Bad activity command, or no activities match: " + cmd);
+ pw.println("Use -h for help.");
+ return;
}
}
@@ -7262,6 +7539,11 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(" Activities waiting to stop:");
dumpHistoryList(pw, mMainStack.mStoppingActivities, " ", "Stop", false);
}
+ if (mMainStack.mGoingToSleepActivities.size() > 0) {
+ pw.println(" ");
+ pw.println(" Activities waiting to sleep:");
+ dumpHistoryList(pw, mMainStack.mGoingToSleepActivities, " ", "Sleep", false);
+ }
if (mMainStack.mFinishingActivities.size() > 0) {
pw.println(" ");
pw.println(" Activities waiting to finish:");
@@ -7273,6 +7555,7 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(" mResumedActivity: " + mMainStack.mResumedActivity);
pw.println(" mFocusedActivity: " + mFocusedActivity);
pw.println(" mLastPausedActivity: " + mMainStack.mLastPausedActivity);
+ pw.println(" mSleepTimeout: " + mMainStack.mSleepTimeout);
if (dumpAll && mRecentTasks.size() > 0) {
pw.println(" ");
@@ -7536,8 +7819,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* - the first arg isn't the flattened component name of an existing service:
* dump all services whose component contains the first arg as a substring
*/
- protected void dumpService(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll) {
+ protected void dumpService(FileDescriptor fd, PrintWriter pw, String[] args, int opti) {
String[] newArgs;
String componentNameString;
ServiceRecord r;
@@ -7557,7 +7839,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (r != null) {
- dumpService(fd, pw, r, newArgs, dumpAll);
+ dumpService(fd, pw, r, newArgs);
} else {
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
synchronized (this) {
@@ -7569,7 +7851,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
for (int i=0; i<services.size(); i++) {
- dumpService(fd, pw, services.get(i), newArgs, dumpAll);
+ dumpService(fd, pw, services.get(i), newArgs);
}
}
}
@@ -7578,16 +7860,10 @@ public final class ActivityManagerService extends ActivityManagerNative
* Invokes IApplicationThread.dumpService() on the thread of the specified service if
* there is a thread associated with the service.
*/
- private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args,
- boolean dumpAll) {
- pw.println(" Service " + r.name.flattenToString());
- if (dumpAll) {
- synchronized (this) {
- pw.print(" * "); pw.println(r);
- r.dump(pw, " ");
- }
- pw.println("");
- }
+ private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args) {
+ pw.println("------------------------------------------------------------"
+ + "-------------------");
+ pw.println("APP SERVICE: " + r.name.flattenToString());
if (r.app != null && r.app.thread != null) {
try {
// flush anything that is already in the PrintWriter since the thread is going
@@ -7602,6 +7878,96 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ /**
+ * There are three things that cmd can be:
+ * - a flattened component name that matched an existing activity
+ * - the cmd arg isn't the flattened component name of an existing activity:
+ * dump all activity whose component contains the cmd as a substring
+ * - A hex number of the ActivityRecord object instance.
+ */
+ protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+ int opti, boolean dumpAll) {
+ String[] newArgs;
+ ComponentName componentName = ComponentName.unflattenFromString(name);
+ int objectId = 0;
+ if (componentName == null) {
+ // Not a '/' separated full component name; maybe an object ID?
+ try {
+ objectId = Integer.parseInt(name, 16);
+ name = null;
+ componentName = null;
+ } catch (RuntimeException e) {
+ }
+ }
+ newArgs = new String[args.length - opti];
+ if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+
+ ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+ synchronized (this) {
+ for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
+ if (componentName != null) {
+ if (r1.intent.getComponent().equals(componentName)) {
+ activities.add(r1);
+ }
+ } else if (name != null) {
+ if (r1.intent.getComponent().flattenToString().contains(name)) {
+ activities.add(r1);
+ }
+ } else if (System.identityHashCode(r1) == objectId) {
+ activities.add(r1);
+ }
+ }
+ }
+
+ if (activities.size() <= 0) {
+ return false;
+ }
+
+ TaskRecord lastTask = null;
+ for (int i=activities.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)activities.get(i);
+ if (lastTask != r.task) {
+ lastTask = r.task;
+ pw.print("* Task "); pw.print(lastTask.affinity);
+ pw.print(" id="); pw.println(lastTask.taskId);
+ if (dumpAll) {
+ lastTask.dump(pw, " ");
+ }
+ }
+ dumpActivity(" ", fd, pw, activities.get(i), newArgs, dumpAll);
+ }
+ return true;
+ }
+
+ /**
+ * Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
+ * there is a thread associated with the activity.
+ */
+ private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
+ ActivityRecord r, String[] args, boolean dumpAll) {
+ synchronized (this) {
+ pw.print(prefix); pw.print("* Activity ");
+ pw.print(Integer.toHexString(System.identityHashCode(r)));
+ pw.print(" "); pw.print(r.shortComponentName); pw.print(" pid=");
+ if (r.app != null) pw.println(r.app.pid);
+ else pw.println("(not running)");
+ if (dumpAll) {
+ r.dump(pw, prefix + " ");
+ }
+ }
+ if (r.app != null && r.app.thread != null) {
+ try {
+ // flush anything that is already in the PrintWriter since the thread is going
+ // to write to the file descriptor directly
+ pw.flush();
+ r.app.thread.dumpActivity(fd, r, prefix + " ", args);
+ pw.flush();
+ } catch (RemoteException e) {
+ pw.println("got a RemoteException while dumping the activity");
+ }
+ }
+ }
+
boolean dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll) {
boolean needSep = false;
@@ -8158,7 +8524,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- if (sr.crashCount >= 2) {
+ if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_PERSISTENT) == 0) {
Slog.w(TAG, "Service crashed " + sr.crashCount
+ " times, stopping: " + sr);
EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
@@ -8566,8 +8933,16 @@ public final class ActivityManagerService extends ActivityManagerNative
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
if (checkComponentPermission(r.permission,
- callingPid, callingUid, r.exported ? -1 : r.appInfo.uid)
+ callingPid, callingUid, r.appInfo.uid, r.exported)
!= PackageManager.PERMISSION_GRANTED) {
+ if (!r.exported) {
+ Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+ + " from pid=" + callingPid
+ + ", uid=" + callingUid
+ + " that is not exported from uid " + r.appInfo.uid);
+ return new ServiceLookupResult(null, "not exported from uid "
+ + r.appInfo.uid);
+ }
Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+ " from pid=" + callingPid
+ ", uid=" + callingUid
@@ -8649,11 +9024,19 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (r != null) {
if (checkComponentPermission(r.permission,
- callingPid, callingUid, r.exported ? -1 : r.appInfo.uid)
+ callingPid, callingUid, r.appInfo.uid, r.exported)
!= PackageManager.PERMISSION_GRANTED) {
+ if (!r.exported) {
+ Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+ + " from pid=" + callingPid
+ + ", uid=" + callingUid
+ + " that is not exported from uid " + r.appInfo.uid);
+ return new ServiceLookupResult(null, "not exported from uid "
+ + r.appInfo.uid);
+ }
Slog.w(TAG, "Permission Denial: Accessing service " + r.name
- + " from pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
+ + " from pid=" + callingPid
+ + ", uid=" + callingUid
+ " requires " + r.permission);
return new ServiceLookupResult(null, r.permission);
}
@@ -8820,6 +9203,11 @@ public final class ActivityManagerService extends ActivityManagerNative
long minDuration = SERVICE_RESTART_DURATION;
long resetTime = SERVICE_RESET_RUN_DURATION;
+ if ((r.serviceInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_PERSISTENT) != 0) {
+ minDuration /= 4;
+ }
+
// Any delivered but not yet finished starts should be put back
// on the pending list.
final int N = r.deliveredStarts.size();
@@ -8859,9 +9247,16 @@ public final class ActivityManagerService extends ActivityManagerNative
r.restartCount = 1;
r.restartDelay = minDuration;
} else {
- r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
- if (r.restartDelay < minDuration) {
- r.restartDelay = minDuration;
+ if ((r.serviceInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_PERSISTENT) != 0) {
+ // Services in peristent processes will restart much more
+ // quickly, since they are pretty important. (Think SystemUI).
+ r.restartDelay += minDuration/2;
+ } else {
+ r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
+ if (r.restartDelay < minDuration) {
+ r.restartDelay = minDuration;
+ }
}
}
}
@@ -10141,7 +10536,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|| uidRemoved) {
if (checkComponentPermission(
android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
- callingPid, callingUid, -1)
+ callingPid, callingUid, -1, true)
== PackageManager.PERMISSION_GRANTED) {
if (uidRemoved) {
final Bundle intentExtras = intent.getExtras();
@@ -10200,6 +10595,15 @@ public final class ActivityManagerService extends ActivityManagerNative
mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
}
+ if (intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction())) {
+ mHandler.sendEmptyMessage(CLEAR_DNS_CACHE);
+ }
+
+ if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) {
+ ProxyProperties proxy = intent.getParcelableExtra("proxy");
+ mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY, proxy));
+ }
+
/*
* Prevent non-system code (defined here to be non-persistent
* processes) from sending protected broadcasts.
@@ -10800,7 +11204,7 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean skip = false;
if (filter.requiredPermission != null) {
int perm = checkComponentPermission(filter.requiredPermission,
- r.callingPid, r.callingUid, -1);
+ r.callingPid, r.callingUid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: broadcasting "
+ r.intent.toString()
@@ -10813,7 +11217,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (r.requiredPermission != null) {
int perm = checkComponentPermission(r.requiredPermission,
- filter.receiverList.pid, filter.receiverList.uid, -1);
+ filter.receiverList.pid, filter.receiverList.uid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: receiving "
+ r.intent.toString()
@@ -11079,17 +11483,26 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean skip = false;
int perm = checkComponentPermission(info.activityInfo.permission,
- r.callingPid, r.callingUid,
- info.activityInfo.exported
- ? -1 : info.activityInfo.applicationInfo.uid);
+ r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
+ info.activityInfo.exported);
if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ")"
- + " requires " + info.activityInfo.permission
- + " due to receiver " + info.activityInfo.packageName
- + "/" + info.activityInfo.name);
+ if (!info.activityInfo.exported) {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ")"
+ + " is not exported from uid " + info.activityInfo.applicationInfo.uid
+ + " due to receiver " + info.activityInfo.packageName
+ + "/" + info.activityInfo.name);
+ } else {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ")"
+ + " requires " + info.activityInfo.permission
+ + " due to receiver " + info.activityInfo.packageName
+ + "/" + info.activityInfo.name);
+ }
skip = true;
}
if (r.callingUid != Process.SYSTEM_UID &&
@@ -11434,14 +11847,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (starting != null) {
kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);
- if (kept) {
- // If this didn't result in the starting activity being
- // destroyed, then we need to make sure at this point that all
- // other activities are made visible.
- if (DEBUG_SWITCH) Slog.i(TAG, "Config didn't destroy " + starting
- + ", ensuring others are correct.");
- mMainStack.ensureActivitiesVisibleLocked(starting, changes);
- }
+ // And we need to make sure at this point that all other activities
+ // are made visible with the correct configuration.
+ mMainStack.ensureActivitiesVisibleLocked(starting, changes);
}
if (values != null && mWindowManager != null) {
@@ -11550,7 +11958,7 @@ public final class ActivityManagerService extends ActivityManagerNative
} else if (app == mHeavyWeightProcess) {
// We don't want to kill the current heavy-weight process.
adj = HEAVY_WEIGHT_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.adjType = "heavy";
} else if (app == mHomeProcess) {
// This process is hosting what we currently consider to be the
@@ -11566,13 +11974,19 @@ public final class ActivityManagerService extends ActivityManagerNative
app.adjType = "bg-activities";
N = app.activities.size();
for (int j=0; j<N; j++) {
- if (app.activities.get(j).visible) {
+ ActivityRecord r = app.activities.get(j);
+ if (r.visible) {
// This app has a visible activity!
app.hidden = false;
adj = VISIBLE_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "visible";
break;
+ } else if (r.state == ActivityState.PAUSING
+ || r.state == ActivityState.PAUSED
+ || r.state == ActivityState.STOPPING) {
+ adj = PERCEPTIBLE_APP_ADJ;
+ app.adjType = "stopping";
}
}
} else {
@@ -12475,7 +12889,69 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
-
+
+ public boolean dumpHeap(String process, boolean managed,
+ String path, ParcelFileDescriptor fd) throws RemoteException {
+
+ try {
+ synchronized (this) {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission (same as profileControl).
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
+ if (fd == null) {
+ throw new IllegalArgumentException("null fd");
+ }
+
+ ProcessRecord proc = null;
+ try {
+ int pid = Integer.parseInt(process);
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ } catch (NumberFormatException e) {
+ }
+
+ if (proc == null) {
+ HashMap<String, SparseArray<ProcessRecord>> all
+ = mProcessNames.getMap();
+ SparseArray<ProcessRecord> procs = all.get(process);
+ if (procs != null && procs.size() > 0) {
+ proc = procs.valueAt(0);
+ }
+ }
+
+ if (proc == null || proc.thread == null) {
+ throw new IllegalArgumentException("Unknown process: " + process);
+ }
+
+ boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
+ if (isSecure) {
+ if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+ throw new SecurityException("Process not debuggable: " + proc);
+ }
+ }
+
+ proc.thread.dumpHeap(managed, path, fd);
+ fd = null;
+ return true;
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Process disappeared");
+ } finally {
+ if (fd != null) {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
/** In this method we try to acquire our lock to make sure that we have not deadlocked */
public void monitor() {
synchronized (this) { }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 4d773e4..0fb30ff 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.Process;
@@ -36,6 +37,7 @@ import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.IApplicationToken;
+import android.view.WindowManager;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -58,7 +60,8 @@ class ActivityRecord extends IApplicationToken.Stub {
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity
final boolean stateNotNeeded; // As per ActivityInfo.flags
- final boolean fullscreen; // covers the full screen?
+ final boolean fullscreen; // covers the full screen?
+ final boolean noDisplay; // activity is not displayed?
final boolean componentSpecified; // did caller specifiy an explicit component?
final boolean isHomeActivity; // do we consider this to be a home activity?
final String baseDir; // where activity source (resources etc) located
@@ -68,6 +71,8 @@ class ActivityRecord extends IApplicationToken.Stub {
int labelRes; // the label information from the package mgr.
int icon; // resource identifier of activity's icon.
int theme; // resource identifier of activity's theme.
+ int realTheme; // actual theme resource we will use, never 0.
+ int windowFlags; // custom window flags for preview window.
TaskRecord task; // the task this is in.
long launchTime; // when we starting launching this activity
long startTime; // last time this activity was started
@@ -98,12 +103,14 @@ class ActivityRecord extends IApplicationToken.Stub {
boolean inHistory; // are we in the history stack?
int launchMode; // the launch mode activity attribute.
boolean visible; // does this activity's window need to be shown?
+ boolean sleeping; // have we told the activity to sleep?
boolean waitingVisible; // true if waiting for a new act to become vis
boolean nowVisible; // is this activity's window visible?
boolean thumbnailNeeded;// has someone requested a thumbnail?
boolean idle; // has the activity gone idle?
boolean hasBeenLaunched;// has this activity ever been launched?
boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
+ boolean immersive; // immersive mode (don't interrupt if possible)
String stringName; // for caching of toString().
@@ -159,12 +166,15 @@ class ActivityRecord extends IApplicationToken.Stub {
pw.print(" finishing="); pw.println(finishing);
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
- pw.print(" launchMode="); pw.println(launchMode);
- pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
pw.print(" visible="); pw.print(visible);
- pw.print(" frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
- pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
+ pw.print(" sleeping="); pw.print(sleeping);
pw.print(" idle="); pw.println(idle);
+ pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
+ pw.print(" noDisplay="); pw.print(noDisplay);
+ pw.print(" immersive="); pw.print(immersive);
+ pw.print(" launchMode="); pw.println(launchMode);
+ pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
+ pw.print(" thumbnailNeeded="); pw.println(thumbnailNeeded);
if (launchTime != 0 || startTime != 0) {
pw.print(prefix); pw.print("launchTime=");
TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime=");
@@ -242,6 +252,16 @@ class ActivityRecord extends IApplicationToken.Stub {
}
icon = aInfo.getIconResource();
theme = aInfo.getThemeResource();
+ realTheme = theme;
+ if (realTheme == 0) {
+ realTheme = aInfo.applicationInfo.targetSdkVersion
+ < Build.VERSION_CODES.HONEYCOMB
+ ? android.R.style.Theme
+ : android.R.style.Theme_Holo;
+ }
+ if ((aInfo.flags&ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
+ windowFlags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ }
if ((aInfo.flags&ActivityInfo.FLAG_MULTIPROCESS) != 0
&& _caller != null
&& (aInfo.applicationInfo.uid == Process.SYSTEM_UID
@@ -259,12 +279,13 @@ class ActivityRecord extends IApplicationToken.Stub {
launchMode = aInfo.launchMode;
AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
- theme != 0 ? theme : android.R.style.Theme,
- com.android.internal.R.styleable.Window);
+ realTheme, com.android.internal.R.styleable.Window);
fullscreen = ent != null && !ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsFloating, false)
&& !ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowIsTranslucent, false);
+ noDisplay = ent != null && ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowNoDisplay, false);
if (!_componentSpecified || _launchedFromUid == Process.myUid()
|| _launchedFromUid == 0) {
@@ -289,6 +310,8 @@ class ActivityRecord extends IApplicationToken.Stub {
} else {
isHomeActivity = false;
}
+
+ immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
} else {
realActivity = null;
taskAffinity = null;
@@ -299,7 +322,18 @@ class ActivityRecord extends IApplicationToken.Stub {
processName = null;
packageName = null;
fullscreen = true;
+ noDisplay = false;
isHomeActivity = false;
+ immersive = false;
+ }
+ }
+
+ void makeFinishing() {
+ if (!finishing) {
+ finishing = true;
+ if (task != null) {
+ task.numActivities--;
+ }
}
}
@@ -403,7 +437,7 @@ class ActivityRecord extends IApplicationToken.Stub {
// an application, and that application is not blocked or unresponding.
// In any other case, we can't count on getting the screen unfrozen,
// so it is best to leave as-is.
- return app == null || (!app.crashing && !app.notResponding);
+ return app != null && !app.crashing && !app.notResponding;
}
public void startFreezingScreenLocked(ProcessRecord app, int configChanges) {
@@ -570,14 +604,32 @@ class ActivityRecord extends IApplicationToken.Stub {
public boolean isInterestingToUserLocked() {
return visible || nowVisible || state == ActivityState.PAUSING ||
state == ActivityState.RESUMED;
- }
+ }
+
+ public void setSleeping(boolean _sleeping) {
+ if (sleeping == _sleeping) {
+ return;
+ }
+ if (app != null && app.thread != null) {
+ try {
+ app.thread.scheduleSleeping(this, _sleeping);
+ if (sleeping && !stack.mGoingToSleepActivities.contains(this)) {
+ stack.mGoingToSleepActivities.add(this);
+ }
+ sleeping = _sleeping;
+ } catch (RemoteException e) {
+ Slog.w(ActivityStack.TAG, "Exception thrown when sleeping: "
+ + intent.getComponent(), e);
+ }
+ }
+ }
public String toString() {
if (stringName != null) {
return stringName;
}
StringBuilder sb = new StringBuilder(128);
- sb.append("HistoryRecord{");
+ sb.append("ActivityRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(' ');
sb.append(intent.getComponent().flattenToShortString());
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index f52d322..2040cbd 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -46,9 +46,10 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -91,6 +92,9 @@ public class ActivityStack {
// next activity.
static final int PAUSE_TIMEOUT = 500;
+ // How long we can hold the sleep wake lock before giving up.
+ static final int SLEEP_TIMEOUT = 5*1000;
+
// How long we can hold the launch wake lock before giving up.
static final int LAUNCH_TIMEOUT = 10*1000;
@@ -99,8 +103,8 @@ public class ActivityStack {
static final int DESTROY_TIMEOUT = 10*1000;
// How long until we reset a task when the user returns to it. Currently
- // 30 minutes.
- static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
+ // disabled.
+ static final long ACTIVITY_INACTIVE_RESET_TIME = 0;
// How long between activity launches that we consider safe to not warn
// the user about an unexpected activity being launched on top.
@@ -157,6 +161,12 @@ public class ActivityStack {
= new ArrayList<ActivityRecord>();
/**
+ * List of activities that are in the process of going to sleep.
+ */
+ final ArrayList<ActivityRecord> mGoingToSleepActivities
+ = new ArrayList<ActivityRecord>();
+
+ /**
* Animations that for the current transition have requested not to
* be considered for the transition animation.
*/
@@ -237,6 +247,15 @@ public class ActivityStack {
long mInitialStartTime = 0;
+ /**
+ * Set when we have taken too long waiting to go to sleep.
+ */
+ boolean mSleepTimeout = false;
+
+ int mThumbnailWidth = -1;
+ int mThumbnailHeight = -1;
+
+ static final int SLEEP_TIMEOUT_MSG = 8;
static final int PAUSE_TIMEOUT_MSG = 9;
static final int IDLE_TIMEOUT_MSG = 10;
static final int IDLE_NOW_MSG = 11;
@@ -251,12 +270,19 @@ public class ActivityStack {
public void handleMessage(Message msg) {
switch (msg.what) {
+ case SLEEP_TIMEOUT_MSG: {
+ if (mService.isSleeping()) {
+ Slog.w(TAG, "Sleep timeout! Sleeping now.");
+ mSleepTimeout = true;
+ checkReadyForSleepLocked();
+ }
+ } break;
case PAUSE_TIMEOUT_MSG: {
IBinder token = (IBinder)msg.obj;
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity pause timeout for " + token);
- activityPaused(token, null, true);
+ activityPaused(token, true);
} break;
case IDLE_TIMEOUT_MSG: {
if (mService.mDidDexOpt) {
@@ -510,6 +536,7 @@ public class ActivityStack {
mService.mHomeProcess = app;
}
mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
+ r.sleeping = false;
app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
System.identityHashCode(r),
r.info, r.icicle, results, newIntents, !andResume,
@@ -564,13 +591,14 @@ public class ActivityStack {
// As part of the process of launching, ActivityThread also performs
// a resume.
r.state = ActivityState.RESUMED;
- r.icicle = null;
- r.haveState = false;
r.stopped = false;
mResumedActivity = r;
r.task.touchActiveTime();
+ if (mMainStack) {
+ mService.addRecentTaskLocked(r.task);
+ }
completeResumeLocked(r);
- pauseIfSleepingLocked();
+ checkReadyForSleepLocked();
} else {
// This activity is not starting in the resumed state... which
// should look like we asked it to pause+stop (but remain visible),
@@ -580,6 +608,9 @@ public class ActivityStack {
r.stopped = true;
}
+ r.icicle = null;
+ r.haveState = false;
+
// Launch the new version setup screen if needed. We do this -after-
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
@@ -623,8 +654,8 @@ public class ActivityStack {
"activity", r.intent.getComponent(), false);
}
- void pauseIfSleepingLocked() {
- if (mService.mSleeping || mService.mShuttingDown) {
+ void stopIfSleepingLocked() {
+ if (mService.isSleeping()) {
if (!mGoingToSleep.isHeld()) {
mGoingToSleep.acquire();
if (mLaunchingActivity.isHeld()) {
@@ -632,18 +663,113 @@ public class ActivityStack {
mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
}
}
+ mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
+ Message msg = mHandler.obtainMessage(SLEEP_TIMEOUT_MSG);
+ mHandler.sendMessageDelayed(msg, SLEEP_TIMEOUT);
+ checkReadyForSleepLocked();
+ }
+ }
- // If we are not currently pausing an activity, get the current
- // one to pause. If we are pausing one, we will just let that stuff
- // run and release the wake lock when all done.
- if (mPausingActivity == null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause...");
+ void awakeFromSleepingLocked() {
+ mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
+ mSleepTimeout = false;
+ if (mGoingToSleep.isHeld()) {
+ mGoingToSleep.release();
+ }
+ // Ensure activities are no longer sleeping.
+ for (int i=mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ r.setSleeping(false);
+ }
+ mGoingToSleepActivities.clear();
+ }
+
+ void activitySleptLocked(ActivityRecord r) {
+ mGoingToSleepActivities.remove(r);
+ checkReadyForSleepLocked();
+ }
+
+ void checkReadyForSleepLocked() {
+ if (!mService.isSleeping()) {
+ // Do not care.
+ return;
+ }
+
+ if (!mSleepTimeout) {
+ if (mResumedActivity != null) {
+ // Still have something resumed; can't sleep until it is paused.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause " + mResumedActivity);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
startPausingLocked(false, true);
+ return;
+ }
+ if (mPausingActivity != null) {
+ // Still waiting for something to pause; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivity);
+ return;
+ }
+
+ if (mStoppingActivities.size() > 0) {
+ // Still need to tell some activities to stop; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to stop "
+ + mStoppingActivities.size() + " activities");
+ Message msg = Message.obtain();
+ msg.what = IDLE_NOW_MSG;
+ mHandler.sendMessage(msg);
+ return;
}
+
+ ensureActivitiesVisibleLocked(null, 0);
+
+ // Make sure any stopped but visible activities are now sleeping.
+ // This ensures that the activity's onStop() is called.
+ for (int i=mHistory.size()-1; i>=0; i--) {
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
+ r.setSleeping(true);
+ }
+ }
+
+ if (mGoingToSleepActivities.size() > 0) {
+ // Still need to tell some activities to sleep; can't sleep yet.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to sleep "
+ + mGoingToSleepActivities.size() + " activities");
+ return;
+ }
+ }
+
+ mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
+
+ if (mGoingToSleep.isHeld()) {
+ mGoingToSleep.release();
+ }
+ if (mService.mShuttingDown) {
+ mService.notifyAll();
}
+
}
+ public final Bitmap screenshotActivities(ActivityRecord who) {
+ if (who.noDisplay) {
+ return null;
+ }
+
+ Resources res = mService.mContext.getResources();
+ int w = mThumbnailWidth;
+ int h = mThumbnailHeight;
+ if (w < 0) {
+ mThumbnailWidth = w =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+ mThumbnailHeight = h =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+ }
+
+ if (w > 0) {
+ return mService.mWindowManager.screenshotApplications(who, w, h);
+ }
+ return null;
+ }
+
private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
if (mPausingActivity != null) {
RuntimeException e = new RuntimeException();
@@ -663,6 +789,10 @@ public class ActivityStack {
mLastPausedActivity = prev;
prev.state = ActivityState.PAUSING;
prev.task.touchActiveTime();
+ prev.thumbnail = screenshotActivities(prev);
+ if (prev.task != null) {
+ prev.task.lastThumbnail = prev.thumbnail;
+ }
mService.updateCpuStats();
@@ -726,10 +856,9 @@ public class ActivityStack {
}
}
- final void activityPaused(IBinder token, Bundle icicle, boolean timeout) {
+ final void activityPaused(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(
- TAG, "Activity paused: token=" + token + ", icicle=" + icicle
- + ", timeout=" + timeout);
+ TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
ActivityRecord r = null;
@@ -737,10 +866,6 @@ public class ActivityStack {
int index = indexOfTokenLocked(token);
if (index >= 0) {
r = (ActivityRecord)mHistory.get(index);
- if (!timeout) {
- r.icicle = icicle;
- r.haveState = true;
- }
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
r.state = ActivityState.PAUSED;
@@ -789,6 +914,8 @@ public class ActivityStack {
Message msg = Message.obtain();
msg.what = IDLE_NOW_MSG;
mHandler.sendMessage(msg);
+ } else {
+ checkReadyForSleepLocked();
}
}
} else {
@@ -798,15 +925,10 @@ public class ActivityStack {
mPausingActivity = null;
}
- if (!mService.mSleeping && !mService.mShuttingDown) {
+ if (!mService.isSleeping()) {
resumeTopActivityLocked(prev);
} else {
- if (mGoingToSleep.isHeld()) {
- mGoingToSleep.release();
- }
- if (mService.mShuttingDown) {
- mService.notifyAll();
- }
+ checkReadyForSleepLocked();
}
if (prev != null) {
@@ -961,6 +1083,7 @@ public class ActivityStack {
TAG, "Making visible and scheduling visibility: " + r);
try {
mService.mWindowManager.setAppVisibility(r, true);
+ r.sleeping = false;
r.app.thread.scheduleWindowVisibility(r, true);
r.stopFreezingScreenLocked(false);
} catch (Exception e) {
@@ -1090,6 +1213,8 @@ public class ActivityStack {
// The activity may be waiting for stop, but that is no longer
// appropriate for it.
mStoppingActivities.remove(next);
+ mGoingToSleepActivities.remove(next);
+ next.sleeping = false;
mWaitingVisibleActivities.remove(next);
if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
@@ -1176,11 +1301,12 @@ public class ActivityStack {
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare close transition: prev=" + prev);
if (mNoAnimActivities.contains(prev)) {
- mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_NONE, false);
} else {
mService.mWindowManager.prepareAppTransition(prev.task == next.task
? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE
- : WindowManagerPolicy.TRANSIT_TASK_CLOSE);
+ : WindowManagerPolicy.TRANSIT_TASK_CLOSE, false);
}
mService.mWindowManager.setAppWillBeHidden(prev);
mService.mWindowManager.setAppVisibility(prev, false);
@@ -1188,11 +1314,12 @@ public class ActivityStack {
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare open transition: prev=" + prev);
if (mNoAnimActivities.contains(next)) {
- mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_NONE, false);
} else {
mService.mWindowManager.prepareAppTransition(prev.task == next.task
? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
- : WindowManagerPolicy.TRANSIT_TASK_OPEN);
+ : WindowManagerPolicy.TRANSIT_TASK_OPEN, false);
}
}
if (false) {
@@ -1203,9 +1330,11 @@ public class ActivityStack {
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare open transition: no previous");
if (mNoAnimActivities.contains(next)) {
- mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_NONE, false);
} else {
- mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN, false);
}
}
@@ -1223,6 +1352,9 @@ public class ActivityStack {
next.state = ActivityState.RESUMED;
mResumedActivity = next;
next.task.touchActiveTime();
+ if (mMainStack) {
+ mService.addRecentTaskLocked(next.task);
+ }
mService.updateLruProcessLocked(next.app, true, true);
updateLRUListLocked(next);
@@ -1284,10 +1416,11 @@ public class ActivityStack {
System.identityHashCode(next),
next.task.taskId, next.shortComponentName);
+ next.sleeping = false;
next.app.thread.scheduleResumeActivity(next,
mService.isNextTransitionForward());
- pauseIfSleepingLocked();
+ checkReadyForSleepLocked();
} catch (Exception e) {
// Whoops, need to restart this activity!
@@ -1301,7 +1434,8 @@ public class ActivityStack {
mService.mWindowManager.setAppStartingWindow(
next, next.packageName, next.theme,
next.nonLocalizedLabel,
- next.labelRes, next.icon, null, true);
+ next.labelRes, next.icon, next.windowFlags,
+ null, true);
}
}
startSpecificActivityLocked(next, true, false);
@@ -1336,7 +1470,8 @@ public class ActivityStack {
mService.mWindowManager.setAppStartingWindow(
next, next.packageName, next.theme,
next.nonLocalizedLabel,
- next.labelRes, next.icon, null, true);
+ next.labelRes, next.icon, next.windowFlags,
+ null, true);
}
if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
}
@@ -1347,7 +1482,7 @@ public class ActivityStack {
}
private final void startActivityLocked(ActivityRecord r, boolean newTask,
- boolean doResume) {
+ boolean doResume, boolean keepCurTransition) {
final int NH = mHistory.size();
int addPos = -1;
@@ -1418,16 +1553,17 @@ public class ActivityStack {
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare open transition: starting " + r);
if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_NONE, keepCurTransition);
mNoAnimActivities.add(r);
} else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
mService.mWindowManager.prepareAppTransition(
- WindowManagerPolicy.TRANSIT_TASK_OPEN);
+ WindowManagerPolicy.TRANSIT_TASK_OPEN, keepCurTransition);
mNoAnimActivities.remove(r);
} else {
mService.mWindowManager.prepareAppTransition(newTask
? WindowManagerPolicy.TRANSIT_TASK_OPEN
- : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN);
+ : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
mNoAnimActivities.remove(r);
}
mService.mWindowManager.addAppToken(
@@ -1460,7 +1596,7 @@ public class ActivityStack {
}
mService.mWindowManager.setAppStartingWindow(
r, r.packageName, r.theme, r.nonLocalizedLabel,
- r.labelRes, r.icon, prev, showStartingIcon);
+ r.labelRes, r.icon, r.windowFlags, prev, showStartingIcon);
}
} else {
// If this is the first activity, don't do any fancy animations,
@@ -1485,7 +1621,8 @@ public class ActivityStack {
ActivityRecord newActivity) {
boolean forceReset = (newActivity.info.flags
&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
- if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
+ if (ACTIVITY_INACTIVE_RESET_TIME > 0
+ && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
if ((newActivity.info.flags
&ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
forceReset = true;
@@ -1571,8 +1708,7 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- target.task = new TaskRecord(mService.mCurTask, target.info, null,
- (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ target.task = new TaskRecord(mService.mCurTask, target.info, null);
target.task.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to new task " + target.task);
@@ -1609,9 +1745,6 @@ public class ActivityStack {
taskTopI = -1;
}
replyChainEnd = -1;
- if (mMainStack) {
- mService.addRecentTaskLocked(target.task);
- }
} else if (forceReset || finishOnTaskLaunch
|| clearWhenTaskReset) {
// If the activity should just be removed -- either
@@ -1774,11 +1907,11 @@ public class ActivityStack {
* activities on top of it and return the instance.
*
* @param newR Description of the new activity being started.
- * @return Returns the old activity that should be continue to be used,
+ * @return Returns the old activity that should be continued to be used,
* or null if none was found.
*/
private final ActivityRecord performClearTaskLocked(int taskId,
- ActivityRecord newR, int launchFlags, boolean doClear) {
+ ActivityRecord newR, int launchFlags) {
int i = mHistory.size();
// First find the requested task.
@@ -1804,17 +1937,18 @@ public class ActivityStack {
if (r.realActivity.equals(newR.realActivity)) {
// Here it is! Now finish everything in front...
ActivityRecord ret = r;
- if (doClear) {
- while (i < (mHistory.size()-1)) {
- i++;
- r = (ActivityRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
- i--;
- }
+ while (i < (mHistory.size()-1)) {
+ i++;
+ r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId != taskId) {
+ break;
+ }
+ if (r.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i--;
}
}
@@ -1841,6 +1975,51 @@ public class ActivityStack {
}
/**
+ * Completely remove all activities associated with an existing task.
+ */
+ private final void performClearTaskLocked(int taskId) {
+ int i = mHistory.size();
+
+ // First find the requested task.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId == taskId) {
+ i++;
+ break;
+ }
+ }
+
+ // Now clear it.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.task.taskId != taskId) {
+ // We hit the bottom. Now finish it all...
+ while (i < (mHistory.size()-1)) {
+ i++;
+ r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId != taskId) {
+ // Whoops hit the end.
+ return;
+ }
+ if (r.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i--;
+ }
+ }
+ return;
+ }
+ }
+ }
+
+ /**
* Find the activity in the history stack within the given task. Returns
* the index within the history at which it's found, or < 0 if not found.
*/
@@ -1880,7 +2059,7 @@ public class ActivityStack {
int grantedMode, ActivityInfo aInfo, IBinder resultTo,
String resultWho, int requestCode,
int callingPid, int callingUid, boolean onlyIfNeeded,
- boolean componentSpecified) {
+ boolean componentSpecified, ActivityRecord[] outActivity) {
int err = START_SUCCESS;
@@ -1958,17 +2137,25 @@ public class ActivityStack {
}
final int perm = mService.checkComponentPermission(aInfo.permission, callingPid,
- callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid);
+ callingUid, aInfo.applicationInfo.uid, aInfo.exported);
if (perm != PackageManager.PERMISSION_GRANTED) {
if (resultRecord != null) {
sendActivityResultLocked(-1,
resultRecord, resultWho, requestCode,
Activity.RESULT_CANCELED, null);
}
- String msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires " + aInfo.permission;
+ String msg;
+ if (!aInfo.exported) {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " not exported from uid " + aInfo.applicationInfo.uid;
+ } else {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires " + aInfo.permission;
+ }
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
@@ -2002,6 +2189,9 @@ public class ActivityStack {
ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
intent, resolvedType, aInfo, mService.mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified);
+ if (outActivity != null) {
+ outActivity[0] = r;
+ }
if (mMainStack) {
if (mResumedActivity == null
@@ -2036,6 +2226,16 @@ public class ActivityStack {
grantedUriPermissions, grantedMode, onlyIfNeeded, true);
}
+ final void moveHomeToFrontFromLaunchLocked(int launchFlags) {
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
+ // Caller wants to appear on home activity, so before starting
+ // their own activity we will bring home to the front.
+ moveHomeToFrontLocked();
+ }
+ }
+
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
int grantedMode, boolean onlyIfNeeded, boolean doResume) {
@@ -2109,6 +2309,7 @@ public class ActivityStack {
}
boolean addingToTask = false;
+ TaskRecord reuseTask = null;
if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
@@ -2139,13 +2340,14 @@ public class ActivityStack {
// being started, which means not bringing it to the front
// if the caller is not itself in the front.
ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
- if (curTop.task != taskTop.task) {
+ if (curTop != null && curTop.task != taskTop.task) {
r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
boolean callerAtFront = sourceRecord == null
|| curTop.task == sourceRecord.task;
if (callerAtFront) {
// We really do want to push this one into the
// user's face, right now.
+ moveHomeToFrontFromLaunchLocked(launchFlags);
moveTaskToFrontLocked(taskTop.task, r);
}
}
@@ -2164,7 +2366,16 @@ public class ActivityStack {
}
return START_RETURN_INTENT_TO_CALLER;
}
- if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
+ // The caller has requested to completely replace any
+ // existing task with its new activity. Well that should
+ // not be too hard...
+ reuseTask = taskTop.task;
+ performClearTaskLocked(taskTop.task.taskId);
+ reuseTask.setIntent(r.intent, r.info);
+ } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// In this situation we want to remove all activities
@@ -2172,7 +2383,7 @@ public class ActivityStack {
// cases this means we are resetting the task to its
// initial state.
ActivityRecord top = performClearTaskLocked(
- taskTop.task.taskId, r, launchFlags, true);
+ taskTop.task.taskId, r, launchFlags);
if (top != null) {
if (top.frontOfTask) {
// Activity aliases may mean we use different
@@ -2233,7 +2444,7 @@ public class ActivityStack {
// for now we'll just drop it.
taskTop.task.setIntent(r.intent, r.info);
}
- if (!addingToTask) {
+ if (!addingToTask && reuseTask == null) {
// We didn't do anything... but it was needed (a.k.a., client
// don't use that intent!) And for paranoia, make
// sure we have correctly resumed the top activity.
@@ -2292,23 +2503,25 @@ public class ActivityStack {
}
boolean newTask = false;
+ boolean keepCurTransition = false;
// Should this be considered a new task?
if (r.resultTo == null && !addingToTask
&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // todo: should do better management of integers.
- mService.mCurTask++;
- if (mService.mCurTask <= 0) {
- mService.mCurTask = 1;
+ if (reuseTask == null) {
+ // todo: should do better management of integers.
+ mService.mCurTask++;
+ if (mService.mCurTask <= 0) {
+ mService.mCurTask = 1;
+ }
+ r.task = new TaskRecord(mService.mCurTask, r.info, intent);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new task " + r.task);
+ } else {
+ r.task = reuseTask;
}
- r.task = new TaskRecord(mService.mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new task " + r.task);
newTask = true;
- if (mMainStack) {
- mService.addRecentTaskLocked(r.task);
- }
+ moveHomeToFrontFromLaunchLocked(launchFlags);
} else if (sourceRecord != null) {
if (!addingToTask &&
@@ -2317,7 +2530,8 @@ public class ActivityStack {
// task, but the caller has asked to clear that task if the
// activity is already running.
ActivityRecord top = performClearTaskLocked(
- sourceRecord.task.taskId, r, launchFlags, true);
+ sourceRecord.task.taskId, r, launchFlags);
+ keepCurTransition = true;
if (top != null) {
logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
top.deliverNewIntentLocked(callingUid, r.intent);
@@ -2359,9 +2573,8 @@ public class ActivityStack {
ActivityRecord prev =
N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
r.task = prev != null
- ? prev.task
- : new TaskRecord(mService.mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ ? prev.task
+ : new TaskRecord(mService.mCurTask, r.info, intent);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new guessed " + r.task);
}
@@ -2380,25 +2593,11 @@ public class ActivityStack {
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
}
logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
- startActivityLocked(r, newTask, doResume);
+ startActivityLocked(r, newTask, doResume, keepCurTransition);
return START_SUCCESS;
}
- final int startActivityMayWait(IApplicationThread caller,
- Intent intent, String resolvedType, Uri[] grantedUriPermissions,
- int grantedMode, IBinder resultTo,
- String resultWho, int requestCode, boolean onlyIfNeeded,
- boolean debug, WaitResult outResult, Configuration config) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
-
+ ActivityInfo resolveActivity(Intent intent, String resolvedType, boolean debug) {
// Collect information about the target of the Intent.
ActivityInfo aInfo;
try {
@@ -2427,11 +2626,32 @@ public class ActivityStack {
}
}
}
+ return aInfo;
+ }
+
+ final int startActivityMayWait(IApplicationThread caller, int callingUid,
+ Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+ int grantedMode, IBinder resultTo,
+ String resultWho, int requestCode, boolean onlyIfNeeded,
+ boolean debug, WaitResult outResult, Configuration config) {
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug);
synchronized (mService) {
int callingPid;
- int callingUid;
- if (caller == null) {
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
callingPid = Binder.getCallingPid();
callingUid = Binder.getCallingUid();
} else {
@@ -2470,8 +2690,8 @@ public class ActivityStack {
IIntentSender target = mService.getIntentSenderLocked(
IActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, null, null, 0, intent,
- resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+ realCallingUid, null, null, 0, new Intent[] { intent },
+ new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT);
Intent newIntent = new Intent();
@@ -2516,7 +2736,7 @@ public class ActivityStack {
int res = startActivityLocked(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, aInfo,
resultTo, resultWho, requestCode, callingPid, callingUid,
- onlyIfNeeded, componentSpecified);
+ onlyIfNeeded, componentSpecified, null);
if (mConfigWillChange && mMainStack) {
// If the caller also wants to switch to a new configuration,
@@ -2567,6 +2787,75 @@ public class ActivityStack {
}
}
+ final int startActivities(IApplicationThread caller, int callingUid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+ if (intents == null) {
+ throw new NullPointerException("intents is null");
+ }
+ if (resolvedTypes == null) {
+ throw new NullPointerException("resolvedTypes is null");
+ }
+ if (intents.length != resolvedTypes.length) {
+ throw new IllegalArgumentException("intents are length different than resolvedTypes");
+ }
+
+ ActivityRecord[] outActivity = new ActivityRecord[1];
+
+ int callingPid;
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
+ callingPid = Binder.getCallingPid();
+ callingUid = Binder.getCallingUid();
+ } else {
+ callingPid = callingUid = -1;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService) {
+
+ for (int i=0; i<intents.length; i++) {
+ Intent intent = intents[i];
+ if (intent == null) {
+ continue;
+ }
+
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], false);
+
+ if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+ throw new IllegalArgumentException(
+ "FLAG_CANT_SAVE_STATE not supported here");
+ }
+
+ int res = startActivityLocked(caller, intent, resolvedTypes[i],
+ null, 0, aInfo, resultTo, null, -1, callingPid, callingUid,
+ false, componentSpecified, outActivity);
+ if (res < 0) {
+ return res;
+ }
+
+ resultTo = outActivity[0];
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return IActivityManager.START_SUCCESS;
+ }
+
void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
long thisTime, long totalTime) {
for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
@@ -2644,6 +2933,9 @@ public class ActivityStack {
mService.mWindowManager.setAppVisibility(r, false);
}
r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags);
+ if (mService.isSleeping()) {
+ r.setSleeping(true);
+ }
} catch (Exception e) {
// Maybe just ignore exceptions here... if the process
// has crashed, our death notification will clean things
@@ -2687,7 +2979,7 @@ public class ActivityStack {
mService.mWindowManager.setAppVisibility(s, false);
}
}
- if (!s.waitingVisible && remove) {
+ if ((!s.waitingVisible || mService.isSleeping()) && remove) {
if (localLOGV) Slog.v(TAG, "Ready to stop: " + s);
if (stops == null) {
stops = new ArrayList<ActivityRecord>();
@@ -2891,7 +3183,7 @@ public class ActivityStack {
return false;
}
- r.finishing = true;
+ r.makeFinishing();
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
System.identityHashCode(r),
r.task.taskId, r.shortComponentName, reason);
@@ -2958,7 +3250,7 @@ public class ActivityStack {
"Prepare close transition: finishing " + r);
mService.mWindowManager.prepareAppTransition(endTask
? WindowManagerPolicy.TRANSIT_TASK_CLOSE
- : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE);
+ : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE, false);
// Tell window manager to prepare for this one to be removed.
mService.mWindowManager.setAppVisibility(r, false);
@@ -3011,6 +3303,8 @@ public class ActivityStack {
Message msg = Message.obtain();
msg.what = IDLE_NOW_MSG;
mHandler.sendMessage(msg);
+ } else {
+ checkReadyForSleepLocked();
}
}
r.state = ActivityState.STOPPING;
@@ -3020,6 +3314,7 @@ public class ActivityStack {
// make sure the record is cleaned out of other places.
mStoppingActivities.remove(r);
+ mGoingToSleepActivities.remove(r);
mWaitingVisibleActivities.remove(r);
if (mResumedActivity == r) {
mResumedActivity = null;
@@ -3096,6 +3391,7 @@ public class ActivityStack {
private final void removeActivityFromHistoryLocked(ActivityRecord r) {
if (r.state != ActivityState.DESTROYED) {
+ r.makeFinishing();
mHistory.remove(r);
r.inHistory = false;
r.state = ActivityState.DESTROYED;
@@ -3246,10 +3542,30 @@ public class ActivityStack {
void removeHistoryRecordsForAppLocked(ProcessRecord app) {
removeHistoryRecordsForAppLocked(mLRUActivities, app);
removeHistoryRecordsForAppLocked(mStoppingActivities, app);
+ removeHistoryRecordsForAppLocked(mGoingToSleepActivities, app);
removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app);
removeHistoryRecordsForAppLocked(mFinishingActivities, app);
}
+ /**
+ * Move the current home activity's task (if one exists) to the front
+ * of the stack.
+ */
+ final void moveHomeToFrontLocked() {
+ TaskRecord homeTask = null;
+ for (int i=mHistory.size()-1; i>=0; i--) {
+ ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+ if (hr.isHomeActivity) {
+ homeTask = hr.task;
+ break;
+ }
+ }
+ if (homeTask != null) {
+ moveTaskToFrontLocked(homeTask, null);
+ }
+ }
+
+
final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
@@ -3274,17 +3590,12 @@ public class ActivityStack {
ActivityRecord r = (ActivityRecord)mHistory.get(pos);
if (localLOGV) Slog.v(
TAG, "At " + pos + " ckp " + r.task + ": " + r);
- boolean first = true;
if (r.task.taskId == task) {
if (localLOGV) Slog.v(TAG, "Removing and adding at " + top);
mHistory.remove(pos);
mHistory.add(top, r);
moved.add(0, r);
top--;
- if (first && mMainStack) {
- mService.addRecentTaskLocked(r.task);
- first = false;
- }
}
pos--;
}
@@ -3293,13 +3604,15 @@ public class ActivityStack {
"Prepare to front transition: task=" + tr);
if (reason != null &&
(reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_NONE, false);
ActivityRecord r = topRunningActivityLocked(null);
if (r != null) {
mNoAnimActivities.add(r);
}
} else {
- mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT);
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_TASK_TO_FRONT, false);
}
mService.mWindowManager.moveAppTokensToTop(moved);
@@ -3378,13 +3691,15 @@ public class ActivityStack {
if (reason != null &&
(reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE);
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_NONE, false);
ActivityRecord r = topRunningActivityLocked(null);
if (r != null) {
mNoAnimActivities.add(r);
}
} else {
- mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK);
+ mService.mWindowManager.prepareAppTransition(
+ WindowManagerPolicy.TRANSIT_TASK_TO_BACK, false);
}
mService.mWindowManager.moveAppTokensToBottom(moved);
if (VALIDATE_TOKENS) {
diff --git a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
index 8e9818d..9fb48b3 100644
--- a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
+++ b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
@@ -49,7 +50,7 @@ class AppWaitingForDebuggerDialog extends BaseErrorDialog {
text.append(" is waiting for the debugger to attach.");
setMessage(text.toString());
- setButton("Force Close", mHandler.obtainMessage(1, app));
+ setButton(DialogInterface.BUTTON_POSITIVE, "Force Close", mHandler.obtainMessage(1, app));
setTitle("Waiting For Debugger");
getWindow().setTitle("Waiting For Debugger: " + app.info.processName);
}
diff --git a/services/java/com/android/server/am/BaseErrorDialog.java b/services/java/com/android/server/am/BaseErrorDialog.java
index 03e3272..d1e89bc 100644
--- a/services/java/com/android/server/am/BaseErrorDialog.java
+++ b/services/java/com/android/server/am/BaseErrorDialog.java
@@ -34,7 +34,7 @@ class BaseErrorDialog extends AlertDialog {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
getWindow().setTitle("Error Dialog");
- setIcon(R.drawable.ic_dialog_alert);
+ setIconAttribute(R.attr.alertDialogIcon);
}
public void onStart() {
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 367c4cf..963a691 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -16,7 +16,9 @@
package com.android.server.am;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Binder;
@@ -46,6 +48,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
final BatteryStatsImpl mStats;
Context mContext;
+ private boolean mBluetoothPendingStats;
+ private BluetoothHeadset mBluetoothHeadset;
BatteryStatsService(String filename) {
mStats = new BatteryStatsImpl(filename);
@@ -287,16 +291,43 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
public void noteBluetoothOn() {
enforceCallingPermission();
- BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+ BluetoothProfile.HEADSET);
+ }
synchronized (mStats) {
- mStats.noteBluetoothOnLocked();
- mStats.setBtHeadset(headset);
+ if (mBluetoothHeadset != null) {
+ mStats.noteBluetoothOnLocked();
+ mStats.setBtHeadset(mBluetoothHeadset);
+ } else {
+ mBluetoothPendingStats = true;
+ }
}
}
-
+
+ private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+ new BluetoothProfile.ServiceListener() {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ mBluetoothHeadset = (BluetoothHeadset) proxy;
+ synchronized (mStats) {
+ if (mBluetoothPendingStats) {
+ mStats.noteBluetoothOnLocked();
+ mStats.setBtHeadset(mBluetoothHeadset);
+ mBluetoothPendingStats = false;
+ }
+ }
+ }
+
+ public void onServiceDisconnected(int profile) {
+ mBluetoothHeadset = null;
+ }
+ };
+
public void noteBluetoothOff() {
enforceCallingPermission();
synchronized (mStats) {
+ mBluetoothPendingStats = false;
mStats.noteBluetoothOffLocked();
}
}
diff --git a/services/java/com/android/server/am/FactoryErrorDialog.java b/services/java/com/android/server/am/FactoryErrorDialog.java
index 2e25474..b19bb5c 100644
--- a/services/java/com/android/server/am/FactoryErrorDialog.java
+++ b/services/java/com/android/server/am/FactoryErrorDialog.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
@@ -26,7 +27,8 @@ class FactoryErrorDialog extends BaseErrorDialog {
setCancelable(false);
setTitle(context.getText(com.android.internal.R.string.factorytest_failed));
setMessage(msg);
- setButton(context.getText(com.android.internal.R.string.factorytest_reboot),
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getText(com.android.internal.R.string.factorytest_reboot),
mHandler.obtainMessage(0));
getWindow().setTitle("Factory Error");
}
diff --git a/services/java/com/android/server/am/LaunchWarningWindow.java b/services/java/com/android/server/am/LaunchWarningWindow.java
index 4130e33..1114a31 100644
--- a/services/java/com/android/server/am/LaunchWarningWindow.java
+++ b/services/java/com/android/server/am/LaunchWarningWindow.java
@@ -4,6 +4,7 @@ import com.android.internal.R;
import android.app.Dialog;
import android.content.Context;
+import android.util.TypedValue;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
@@ -20,8 +21,11 @@ public class LaunchWarningWindow extends Dialog {
setContentView(R.layout.launch_warning);
setTitle(context.getText(R.string.launch_warning_title));
- getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
- R.drawable.ic_dialog_alert);
+
+ TypedValue out = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.alertDialogIcon, out, true);
+ getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, out.resourceId);
+
ImageView icon = (ImageView)findViewById(R.id.replace_app_icon);
icon.setImageDrawable(next.info.applicationInfo.loadIcon(context.getPackageManager()));
TextView text = (TextView)findViewById(R.id.replace_message);
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 7a85eb8..ee6e420 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -47,20 +47,24 @@ class PendingIntentRecord extends IIntentSender.Stub {
final int requestCode;
final Intent requestIntent;
final String requestResolvedType;
+ Intent[] allIntents;
+ String[] allResolvedTypes;
final int flags;
final int hashCode;
private static final int ODD_PRIME_NUMBER = 37;
Key(int _t, String _p, ActivityRecord _a, String _w,
- int _r, Intent _i, String _it, int _f) {
+ int _r, Intent[] _i, String[] _it, int _f) {
type = _t;
packageName = _p;
activity = _a;
who = _w;
requestCode = _r;
- requestIntent = _i;
- requestResolvedType = _it;
+ requestIntent = _i != null ? _i[_i.length-1] : null;
+ requestResolvedType = _it != null ? _it[_it.length-1] : null;
+ allIntents = _i;
+ allResolvedTypes = _it;
flags = _f;
int hash = 23;
@@ -72,11 +76,11 @@ class PendingIntentRecord extends IIntentSender.Stub {
if (_a != null) {
hash = (ODD_PRIME_NUMBER*hash) + _a.hashCode();
}
- if (_i != null) {
- hash = (ODD_PRIME_NUMBER*hash) + _i.filterHashCode();
+ if (requestIntent != null) {
+ hash = (ODD_PRIME_NUMBER*hash) + requestIntent.filterHashCode();
}
- if (_it != null) {
- hash = (ODD_PRIME_NUMBER*hash) + _it.hashCode();
+ if (requestResolvedType != null) {
+ hash = (ODD_PRIME_NUMBER*hash) + requestResolvedType.hashCode();
}
hash = (ODD_PRIME_NUMBER*hash) + _p.hashCode();
hash = (ODD_PRIME_NUMBER*hash) + _t;
@@ -209,9 +213,24 @@ class PendingIntentRecord extends IIntentSender.Stub {
switch (key.type) {
case IActivityManager.INTENT_SENDER_ACTIVITY:
try {
- owner.startActivityInPackage(uid,
- finalIntent, resolvedType,
- resultTo, resultWho, requestCode, false);
+ if (key.allIntents != null && key.allIntents.length > 1) {
+ Intent[] allIntents = new Intent[key.allIntents.length];
+ String[] allResolvedTypes = new String[key.allIntents.length];
+ System.arraycopy(key.allIntents, 0, allIntents, 0,
+ key.allIntents.length);
+ if (key.allResolvedTypes != null) {
+ System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0,
+ key.allResolvedTypes.length);
+ }
+ allIntents[allIntents.length-1] = finalIntent;
+ allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
+ owner.startActivitiesInPackage(uid, allIntents,
+ allResolvedTypes, resultTo);
+ } else {
+ owner.startActivityInPackage(uid,
+ finalIntent, resolvedType,
+ resultTo, resultWho, requestCode, false);
+ }
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Unable to send startActivity intent", e);
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index bcb8f54..86cec42 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -19,14 +19,13 @@ package com.android.server.am;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.os.SystemClock;
+import android.graphics.Bitmap;
import java.io.PrintWriter;
class TaskRecord {
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
- final boolean clearOnBackground; // As per the original activity.
Intent intent; // The original intent that started the task.
Intent affinityIntent; // Intent of affinity-moved activity that started this task.
ComponentName origActivity; // The non-alias activity component of the intent.
@@ -35,14 +34,14 @@ class TaskRecord {
long lastActiveTime; // Last time this task was active, including sleep.
boolean rootWasReset; // True if the intent at the root of the task had
// the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
+ Bitmap lastThumbnail; // Last thumbnail captured for this task.
+ CharSequence lastDescription; // Last description captured for this task.
String stringName; // caching of toString() result.
- TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
- boolean _clearOnBackground) {
+ TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
taskId = _taskId;
affinity = info.taskAffinity;
- clearOnBackground = _clearOnBackground;
setIntent(_intent, info);
}
@@ -86,9 +85,8 @@ class TaskRecord {
}
void dump(PrintWriter pw, String prefix) {
- if (clearOnBackground || numActivities != 0 || rootWasReset) {
- pw.print(prefix); pw.print("clearOnBackground="); pw.print(clearOnBackground);
- pw.print(" numActivities="); pw.print(numActivities);
+ if (numActivities != 0 || rootWasReset) {
+ pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
pw.print(" rootWasReset="); pw.println(rootWasReset);
}
if (affinity != null) {
diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java
index 99c82e6..68a2e0f 100644
--- a/services/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -45,7 +45,7 @@ class UriPermissionOwner {
}
Binder getExternalTokenLocked() {
- if (externalToken != null) {
+ if (externalToken == null) {
externalToken = new ExternalToken();
}
return externalToken;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index a73a4ce..dd9db9a 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -19,20 +19,19 @@ package com.android.server.connectivity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothPan;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.hardware.Usb;
+import android.hardware.UsbManager;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkInfo;
-import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -52,8 +51,10 @@ import com.android.internal.util.HierarchicalStateMachine;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.Set;
/**
* @hide
@@ -66,7 +67,8 @@ import java.util.Set;
public class Tethering extends INetworkManagementEventObserver.Stub {
private Context mContext;
- private final String TAG = "Tethering";
+ private final static String TAG = "Tethering";
+ private final static boolean DEBUG = true;
private boolean mBooted = false;
//used to remember if we got connected before boot finished
@@ -75,6 +77,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// TODO - remove both of these - should be part of interface inspection/selection stuff
private String[] mTetherableUsbRegexs;
private String[] mTetherableWifiRegexs;
+ private String[] mTetherableBluetoothRegexs;
private String[] mUpstreamIfaceRegexs;
private Looper mLooper;
@@ -87,17 +90,31 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
private static final String USB_NETMASK = "255.255.255.0";
- // FYI - the default wifi is 192.168.43.1 and 255.255.255.0
+ // USB is 192.168.42.1 and 255.255.255.0
+ // Wifi is 192.168.43.1 and 255.255.255.0
+ // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
+ // with 255.255.255.0
private String[] mDhcpRange;
private static final String DHCP_DEFAULT_RANGE1_START = "192.168.42.2";
private static final String DHCP_DEFAULT_RANGE1_STOP = "192.168.42.254";
private static final String DHCP_DEFAULT_RANGE2_START = "192.168.43.2";
private static final String DHCP_DEFAULT_RANGE2_STOP = "192.168.43.254";
+ private static final String DHCP_DEFAULT_RANGE3_START = "192.168.44.2";
+ private static final String DHCP_DEFAULT_RANGE3_STOP = "192.168.44.254";
+ private static final String DHCP_DEFAULT_RANGE4_START = "192.168.45.2";
+ private static final String DHCP_DEFAULT_RANGE4_STOP = "192.168.45.254";
+ private static final String DHCP_DEFAULT_RANGE5_START = "192.168.46.2";
+ private static final String DHCP_DEFAULT_RANGE5_STOP = "192.168.46.254";
+ private static final String DHCP_DEFAULT_RANGE6_START = "192.168.47.2";
+ private static final String DHCP_DEFAULT_RANGE6_STOP = "192.168.47.254";
+ private static final String DHCP_DEFAULT_RANGE7_START = "192.168.48.2";
+ private static final String DHCP_DEFAULT_RANGE7_STOP = "192.168.48.254";
+
private String[] mDnsServers;
private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
- private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2";
+ private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4";
// resampled each time we turn on tethering - used as cache for settings/config-val
private boolean mDunRequired; // configuration info - must use DUN apn on 3g
@@ -111,14 +128,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private boolean mUsbMassStorageOff; // track the status of USB Mass Storage
private boolean mUsbConnected; // track the status of USB connection
- // mUsbHandler message
- static final int USB_STATE_CHANGE = 1;
- static final int USB_DISCONNECTED = 0;
- static final int USB_CONNECTED = 1;
-
- // Time to delay before processing USB disconnect events
- static final long USB_DISCONNECT_DELAY = 1000;
-
public Tethering(Context context, Looper looper) {
mContext = context;
mLooper = looper;
@@ -143,7 +152,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mStateReceiver = new StateReceiver();
IntentFilter filter = new IntentFilter();
- filter.addAction(Usb.ACTION_USB_STATE);
+ filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(mStateReceiver, filter);
@@ -160,11 +169,21 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mDhcpRange = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_dhcp_range);
if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
- mDhcpRange = new String[4];
+ mDhcpRange = new String[14];
mDhcpRange[0] = DHCP_DEFAULT_RANGE1_START;
mDhcpRange[1] = DHCP_DEFAULT_RANGE1_STOP;
mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START;
mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP;
+ mDhcpRange[4] = DHCP_DEFAULT_RANGE3_START;
+ mDhcpRange[5] = DHCP_DEFAULT_RANGE3_STOP;
+ mDhcpRange[6] = DHCP_DEFAULT_RANGE4_START;
+ mDhcpRange[7] = DHCP_DEFAULT_RANGE4_STOP;
+ mDhcpRange[8] = DHCP_DEFAULT_RANGE5_START;
+ mDhcpRange[9] = DHCP_DEFAULT_RANGE5_STOP;
+ mDhcpRange[10] = DHCP_DEFAULT_RANGE6_START;
+ mDhcpRange[11] = DHCP_DEFAULT_RANGE6_STOP;
+ mDhcpRange[12] = DHCP_DEFAULT_RANGE7_START;
+ mDhcpRange[13] = DHCP_DEFAULT_RANGE7_STOP;
}
mDunRequired = false; // resample when we turn on
@@ -172,6 +191,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
com.android.internal.R.array.config_tether_usb_regexs);
mTetherableWifiRegexs = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_wifi_regexs);
+ mTetherableBluetoothRegexs = context.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_bluetooth_regexs);
mUpstreamIfaceRegexs = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_upstream_regexs);
@@ -182,7 +203,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
public void interfaceLinkStatusChanged(String iface, boolean link) {
- Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
+ if (DEBUG) Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
boolean found = false;
boolean usb = false;
if (isWifi(iface)) {
@@ -190,6 +211,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
} else if (isUsb(iface)) {
found = true;
usb = true;
+ } else if (isBluetooth(iface)) {
+ found = true;
}
if (found == false) return;
@@ -224,6 +247,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return false;
}
+ public boolean isBluetooth(String iface) {
+ for (String regex : mTetherableBluetoothRegexs) {
+ if (iface.matches(regex)) return true;
+ }
+ return false;
+ }
public void interfaceAdded(String iface) {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
@@ -236,29 +265,34 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
found = true;
usb = true;
}
+ if (isBluetooth(iface)) {
+ found = true;
+ }
if (found == false) {
- Log.d(TAG, iface + " is not a tetherable iface, ignoring");
+ if (DEBUG) Log.d(TAG, iface + " is not a tetherable iface, ignoring");
return;
}
synchronized (mIfaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm != null) {
- Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring");
+ if (DEBUG) Log.d(TAG, "active iface (" + iface + ") reported as added, ignoring");
return;
}
sm = new TetherInterfaceSM(iface, mLooper, usb);
mIfaces.put(iface, sm);
sm.start();
}
- Log.d(TAG, "interfaceAdded :" + iface);
+ if (DEBUG) Log.d(TAG, "interfaceAdded :" + iface);
}
public void interfaceRemoved(String iface) {
synchronized (mIfaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm == null) {
- Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring");
+ if (DEBUG) {
+ Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring");
+ }
return;
}
sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN);
@@ -329,13 +363,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
boolean wifiTethered = false;
boolean usbTethered = false;
+ boolean bluetoothTethered = false;
synchronized (mIfaces) {
Set ifaces = mIfaces.keySet();
for (Object iface : ifaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm != null) {
- if(sm.isErrored()) {
+ if (sm.isErrored()) {
erroredList.add((String)iface);
} else if (sm.isAvailable()) {
availableList.add((String)iface);
@@ -344,6 +379,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
usbTethered = true;
} else if (isWifi((String)iface)) {
wifiTethered = true;
+ } else if (isBluetooth((String)iface)) {
+ bluetoothTethered = true;
}
activeList.add((String)iface);
}
@@ -358,17 +395,25 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
erroredList);
mContext.sendStickyBroadcast(broadcast);
- Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
- activeList.size() + ", " + erroredList.size());
+ if (DEBUG) {
+ Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
+ activeList.size() + ", " + erroredList.size());
+ }
if (usbTethered) {
- if (wifiTethered) {
+ if (wifiTethered || bluetoothTethered) {
showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_general);
} else {
showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_usb);
}
} else if (wifiTethered) {
- showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi);
+ if (bluetoothTethered) {
+ showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_general);
+ } else {
+ showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi);
+ }
+ } else if (bluetoothTethered) {
+ showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_bluetooth);
} else {
clearTetheredNotification();
}
@@ -399,7 +444,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
CharSequence message = r.getText(com.android.internal.R.string.
tethered_notification_message);
- if(mTetheredNotification == null) {
+ if (mTetheredNotification == null) {
mTetheredNotification = new Notification();
mTetheredNotification.when = 0;
}
@@ -429,25 +474,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
- private Handler mUsbHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- mUsbConnected = (msg.arg1 == USB_CONNECTED);
- updateUsbStatus();
- }
- };
-
private class StateReceiver extends BroadcastReceiver {
public void onReceive(Context content, Intent intent) {
String action = intent.getAction();
- if (action.equals(Usb.ACTION_USB_STATE)) {
- // process connect events immediately, but delay handling disconnects
- // to debounce USB configuration changes
- boolean connected = intent.getExtras().getBoolean(Usb.USB_CONNECTED);
- Message msg = Message.obtain(mUsbHandler, USB_STATE_CHANGE,
- (connected ? USB_CONNECTED : USB_DISCONNECTED), 0);
- mUsbHandler.removeMessages(USB_STATE_CHANGE);
- mUsbHandler.sendMessageDelayed(msg, connected ? 0 : USB_DISCONNECT_DELAY);
+ if (action.equals(UsbManager.ACTION_USB_STATE)) {
+ mUsbConnected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
+ updateUsbStatus();
} else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
mUsbMassStorageOff = false;
updateUsbStatus();
@@ -456,6 +488,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mUsbMassStorageOff = true;
updateUsbStatus();
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ if (DEBUG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBooted = true;
@@ -486,9 +519,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
- // toggled when we enter/leave the fully teathered state
+ // toggled when we enter/leave the fully tethered state
private boolean enableUsbRndis(boolean enabled) {
- Log.d(TAG, "enableUsbRndis(" + enabled + ")");
+ if (DEBUG) Log.d(TAG, "enableUsbRndis(" + enabled + ")");
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
@@ -513,7 +546,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// configured when we start tethering and unconfig'd on error or conclusion
private boolean configureUsbIface(boolean enabled) {
- Log.d(TAG, "configureUsbIface(" + enabled + ")");
+ if (DEBUG) Log.d(TAG, "configureUsbIface(" + enabled + ")");
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
@@ -532,16 +565,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
try {
ifcg = service.getInterfaceConfig(iface);
if (ifcg != null) {
- String[] addr = USB_NEAR_IFACE_ADDR.split("\\.");
- ifcg.ipAddr = (Integer.parseInt(addr[0]) << 24) +
- (Integer.parseInt(addr[1]) << 16) +
- (Integer.parseInt(addr[2]) << 8) +
- (Integer.parseInt(addr[3]));
- addr = USB_NETMASK.split("\\.");
- ifcg.netmask = (Integer.parseInt(addr[0]) << 24) +
- (Integer.parseInt(addr[1]) << 16) +
- (Integer.parseInt(addr[2]) << 8) +
- (Integer.parseInt(addr[3]));
+ ifcg.addr = InetAddress.getByName(USB_NEAR_IFACE_ADDR);
+ ifcg.mask = InetAddress.getByName(USB_NETMASK);
if (enabled) {
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
} else {
@@ -569,6 +594,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return mTetherableWifiRegexs;
}
+ public String[] getTetherableBluetoothRegexs() {
+ return mTetherableBluetoothRegexs;
+ }
+
public String[] getUpstreamIfaceRegexs() {
return mUpstreamIfaceRegexs;
}
@@ -760,7 +789,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
@Override
public boolean processMessage(Message message) {
- Log.d(TAG, "InitialState.processMessage what=" + message.what);
+ if (DEBUG) Log.d(TAG, "InitialState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
@@ -801,7 +830,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
@Override
public boolean processMessage(Message message) {
- Log.d(TAG, "StartingState.processMessage what=" + message.what);
+ if (DEBUG) Log.d(TAG, "StartingState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
// maybe a parent class?
@@ -853,7 +882,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return;
}
if (mUsb) Tethering.this.enableUsbRndis(true);
- Log.d(TAG, "Tethered " + mIfaceName);
+ if (DEBUG) Log.d(TAG, "Tethered " + mIfaceName);
setAvailable(false);
setTethered(true);
sendTetherStateChangedBroadcast();
@@ -864,7 +893,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
@Override
public boolean processMessage(Message message) {
- Log.d(TAG, "TetheredState.processMessage what=" + message.what);
+ if (DEBUG) Log.d(TAG, "TetheredState.processMessage what=" + message.what);
boolean retValue = true;
boolean error = false;
switch (message.what) {
@@ -907,13 +936,18 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
} else if (message.what == CMD_INTERFACE_DOWN) {
transitionTo(mUnavailableState);
}
- Log.d(TAG, "Untethered " + mIfaceName);
+ if (DEBUG) Log.d(TAG, "Untethered " + mIfaceName);
break;
case CMD_TETHER_CONNECTION_CHANGED:
String newUpstreamIfaceName = (String)(message.obj);
b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
service = INetworkManagementService.Stub.asInterface(b);
-
+ if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
+ (mMyUpstreamIfaceName != null &&
+ mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
+ if (DEBUG) Log.d(TAG, "Connection changed noop - dropping");
+ break;
+ }
if (mMyUpstreamIfaceName != null) {
try {
service.disableNat(mIfaceName, mMyUpstreamIfaceName);
@@ -980,7 +1014,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
break;
}
- Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
+ if (DEBUG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
sendTetherStateChangedBroadcast();
if (mUsb) {
if (!Tethering.this.configureUsbIface(false)) {
@@ -1056,7 +1090,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private ArrayList mNotifyList;
- private boolean mConnectionRequested = false;
+ private int mCurrentConnectionSequence;
+ private boolean mMobileReserved = false;
private String mUpstreamIfaceName = null;
@@ -1095,43 +1130,47 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public boolean processMessage(Message m) {
return false;
}
- protected int turnOnMobileConnection() {
+ protected boolean turnOnMobileConnection() {
+ boolean retValue = true;
+ if (mMobileReserved) return retValue;
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- int retValue = Phone.APN_REQUEST_FAILED;
+ int result = Phone.APN_REQUEST_FAILED;
try {
- retValue = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
- (mDunRequired ? Phone.FEATURE_ENABLE_DUN : Phone.FEATURE_ENABLE_HIPRI),
- new Binder());
+ result = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ (mDunRequired ? Phone.FEATURE_ENABLE_DUN_ALWAYS :
+ Phone.FEATURE_ENABLE_HIPRI), new Binder());
} catch (Exception e) {
}
- switch (retValue) {
+ switch (result) {
case Phone.APN_ALREADY_ACTIVE:
case Phone.APN_REQUEST_STARTED:
- sendMessageDelayed(CMD_CELL_CONNECTION_RENEW, CELL_CONNECTION_RENEW_MS);
- mConnectionRequested = true;
+ mMobileReserved = true;
+ Message m = obtainMessage(CMD_CELL_CONNECTION_RENEW);
+ m.arg1 = ++mCurrentConnectionSequence;
+ sendMessageDelayed(m, CELL_CONNECTION_RENEW_MS);
break;
case Phone.APN_REQUEST_FAILED:
default:
- mConnectionRequested = false;
+ retValue = false;
break;
}
return retValue;
}
protected boolean turnOffMobileConnection() {
- if (mConnectionRequested) {
+ if (mMobileReserved) {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service =
IConnectivityManager.Stub.asInterface(b);
try {
service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
- (mDunRequired? Phone.FEATURE_ENABLE_DUN :
+ (mDunRequired? Phone.FEATURE_ENABLE_DUN_ALWAYS :
Phone.FEATURE_ENABLE_HIPRI));
} catch (Exception e) {
return false;
}
- mConnectionRequested = false;
+ mMobileReserved = false;
}
return true;
}
@@ -1194,32 +1233,35 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
for (String iface : ifaces) {
for (String regex : mUpstreamIfaceRegexs) {
if (iface.matches(regex)) {
- // verify it is up!
+ // verify it is active
InterfaceConfiguration ifcg = null;
try {
ifcg = service.getInterfaceConfig(iface);
+ if (ifcg.isActive()) {
+ return iface;
+ }
} catch (Exception e) {
Log.e(TAG, "Error getting iface config :" + e);
// ignore - try next
continue;
}
- if (ifcg.interfaceFlags.contains("up")) {
- return iface;
- }
}
}
}
return null;
}
+
protected void chooseUpstreamType(boolean tryCell) {
// decide if the current upstream is good or not and if not
// do something about it (start up DUN if required or HiPri if not)
String iface = findActiveUpstreamIface();
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
- mConnectionRequested = false;
- Log.d(TAG, "chooseUpstreamType(" + tryCell + "), dunRequired ="
- + mDunRequired + ", iface=" + iface);
+ mMobileReserved = false;
+ if (DEBUG) {
+ Log.d(TAG, "chooseUpstreamType(" + tryCell + "), dunRequired ="
+ + mDunRequired + ", iface=" + iface);
+ }
if (iface != null) {
try {
if (mDunRequired) {
@@ -1227,7 +1269,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
NetworkInfo info = cm.getNetworkInfo(
ConnectivityManager.TYPE_MOBILE_DUN);
if (info.isConnected()) {
- Log.d(TAG, "setting dun ifacename =" + iface);
+ if (DEBUG) Log.d(TAG, "setting dun ifacename =" + iface);
// even if we're already connected - it may be somebody else's
// refcount, so add our own
turnOnMobileConnection();
@@ -1239,11 +1281,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
} else {
- Log.d(TAG, "checking if hipri brought us this connection");
+ if (DEBUG) Log.d(TAG, "checking if hipri brought us this connection");
NetworkInfo info = cm.getNetworkInfo(
ConnectivityManager.TYPE_MOBILE_HIPRI);
if (info.isConnected()) {
- Log.d(TAG, "yes - hipri in use");
+ if (DEBUG) Log.d(TAG, "yes - hipri in use");
// even if we're already connected - it may be sombody else's
// refcount, so add our own
turnOnMobileConnection();
@@ -1256,16 +1298,19 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
// may have been set to null in the if above
if (iface == null ) {
+ boolean success = false;
if (tryCell == TRY_TO_SETUP_MOBILE_CONNECTION) {
- turnOnMobileConnection();
+ success = turnOnMobileConnection();
+ }
+ if (!success) {
+ // wait for things to settle and retry
+ sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
- // wait for things to settle and retry
- sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
notifyTetheredOfNewUpstreamIface(iface);
}
protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
- Log.d(TAG, "notifying tethered with iface =" + ifaceName);
+ if (DEBUG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
mUpstreamIfaceName = ifaceName;
for (Object o : mNotifyList) {
TetherInterfaceSM sm = (TetherInterfaceSM)o;
@@ -1278,23 +1323,23 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
class InitialState extends TetherMasterUtilState {
@Override
public void enter() {
- mConnectionRequested = false;
+ mMobileReserved = false;
}
@Override
public boolean processMessage(Message message) {
- Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
+ if (DEBUG) Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
mDunRequired = isDunRequired();
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
- Log.d(TAG, "Tether Mode requested by " + who.toString());
+ if (DEBUG) Log.d(TAG, "Tether Mode requested by " + who.toString());
mNotifyList.add(who);
transitionTo(mTetherModeAliveState);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
- Log.d(TAG, "Tether Mode unrequested by " + who.toString());
+ if (DEBUG) Log.d(TAG, "Tether Mode unrequested by " + who.toString());
int index = mNotifyList.indexOf(who);
if (index != -1) {
mNotifyList.remove(who);
@@ -1309,10 +1354,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
class TetherModeAliveState extends TetherMasterUtilState {
- boolean mTryCell = WAIT_FOR_NETWORK_TO_SETTLE;
+ boolean mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE;
@Override
public void enter() {
- mTryCell = WAIT_FOR_NETWORK_TO_SETTLE; // first pass lets just see what we have.
+ mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE; // better try something first pass
+ // or crazy tests cases will fail
chooseUpstreamType(mTryCell);
mTryCell = !mTryCell;
turnOnMasterTetherSettings(); // may transition us out
@@ -1324,7 +1370,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
@Override
public boolean processMessage(Message message) {
- Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what);
+ if (DEBUG) Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
@@ -1351,9 +1397,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
case CMD_CELL_CONNECTION_RENEW:
// make sure we're still using a requested connection - may have found
// wifi or something since then.
- if (mConnectionRequested) {
- Log.d(TAG, "renewing mobile connection - requeuing for another " +
- CELL_CONNECTION_RENEW_MS + "ms");
+ if (mCurrentConnectionSequence == message.arg1) {
+ if (DEBUG) {
+ Log.d(TAG, "renewing mobile connection - requeuing for another " +
+ CELL_CONNECTION_RENEW_MS + "ms");
+ }
+ mMobileReserved = false; // need to renew it
turnOnMobileConnection();
}
break;
diff --git a/services/java/com/android/server/location/ComprehensiveCountryDetector.java b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
new file mode 100755
index 0000000..e9ce3ce
--- /dev/null
+++ b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
@@ -0,0 +1,359 @@
+/*
+ * 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.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.Geocoder;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import java.util.Locale;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * This class is used to detect the country where the user is. The sources of
+ * country are queried in order of reliability, like
+ * <ul>
+ * <li>Mobile network</li>
+ * <li>Location</li>
+ * <li>SIM's country</li>
+ * <li>Phone's locale</li>
+ * </ul>
+ * <p>
+ * Call the {@link #detectCountry()} to get the available country immediately.
+ * <p>
+ * To be notified of the future country change, using the
+ * {@link #setCountryListener(CountryListener)}
+ * <p>
+ * Using the {@link #stop()} to stop listening to the country change.
+ * <p>
+ * The country information will be refreshed every
+ * {@link #LOCATION_REFRESH_INTERVAL} once the location based country is used.
+ *
+ * @hide
+ */
+public class ComprehensiveCountryDetector extends CountryDetectorBase {
+
+ private final static String TAG = "ComprehensiveCountryDetector";
+
+ /**
+ * The refresh interval when the location based country was used
+ */
+ private final static long LOCATION_REFRESH_INTERVAL = 1000 * 60 * 60 * 24; // 1 day
+
+ protected CountryDetectorBase mLocationBasedCountryDetector;
+ protected Timer mLocationRefreshTimer;
+
+ private final int mPhoneType;
+ private Country mCountry;
+ private TelephonyManager mTelephonyManager;
+ private Country mCountryFromLocation;
+ private boolean mStopped = false;
+ private ServiceState mLastState;
+
+ private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ // TODO: Find out how often we will be notified, if this method is called too
+ // many times, let's consider querying the network.
+ Slog.d(TAG, "onServiceStateChanged");
+ // We only care the state change
+ if (mLastState == null || mLastState.getState() != serviceState.getState()) {
+ detectCountry(true, true);
+ mLastState = new ServiceState(serviceState);
+ }
+ }
+ };
+
+ /**
+ * The listener for receiving the notification from LocationBasedCountryDetector.
+ */
+ private CountryListener mLocationBasedCountryDetectionListener = new CountryListener() {
+ public void onCountryDetected(Country country) {
+ mCountryFromLocation = country;
+ // Don't start the LocationBasedCountryDetector.
+ detectCountry(true, false);
+ stopLocationBasedDetector();
+ }
+ };
+
+ public ComprehensiveCountryDetector(Context context) {
+ super(context);
+ mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ mPhoneType = mTelephonyManager.getPhoneType();
+ }
+
+ @Override
+ public Country detectCountry() {
+ // Don't start the LocationBasedCountryDetector if we have been stopped.
+ return detectCountry(false, !mStopped);
+ }
+
+ @Override
+ public void stop() {
+ Slog.i(TAG, "Stop the detector.");
+ cancelLocationRefresh();
+ removePhoneStateListener();
+ stopLocationBasedDetector();
+ mListener = null;
+ mStopped = true;
+ }
+
+ /**
+ * Get the country from different sources in order of the reliability.
+ */
+ private Country getCountry() {
+ Country result = null;
+ result = getNetworkBasedCountry();
+ if (result == null) {
+ result = getLastKnownLocationBasedCountry();
+ }
+ if (result == null) {
+ result = getSimBasedCountry();
+ }
+ if (result == null) {
+ result = getLocaleCountry();
+ }
+ return result;
+ }
+
+ /**
+ * @return the country from the mobile network.
+ */
+ protected Country getNetworkBasedCountry() {
+ String countryIso = null;
+ // TODO: The document says the result may be unreliable on CDMA networks. Shall we use
+ // it on CDMA phone? We may test the Android primarily used countries.
+ if (mPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
+ countryIso = mTelephonyManager.getNetworkCountryIso();
+ if (!TextUtils.isEmpty(countryIso)) {
+ return new Country(countryIso, Country.COUNTRY_SOURCE_NETWORK);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the cached location based country.
+ */
+ protected Country getLastKnownLocationBasedCountry() {
+ return mCountryFromLocation;
+ }
+
+ /**
+ * @return the country from SIM card
+ */
+ protected Country getSimBasedCountry() {
+ String countryIso = null;
+ countryIso = mTelephonyManager.getSimCountryIso();
+ if (!TextUtils.isEmpty(countryIso)) {
+ return new Country(countryIso, Country.COUNTRY_SOURCE_SIM);
+ }
+ return null;
+ }
+
+ /**
+ * @return the country from the system's locale.
+ */
+ protected Country getLocaleCountry() {
+ Locale defaultLocale = Locale.getDefault();
+ if (defaultLocale != null) {
+ return new Country(defaultLocale.getCountry(), Country.COUNTRY_SOURCE_LOCALE);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @param notifyChange indicates whether the listener should be notified the change of the
+ * country
+ * @param startLocationBasedDetection indicates whether the LocationBasedCountryDetector could
+ * be started if the current country source is less reliable than the location.
+ * @return the current available UserCountry
+ */
+ private Country detectCountry(boolean notifyChange, boolean startLocationBasedDetection) {
+ Country country = getCountry();
+ runAfterDetectionAsync(mCountry != null ? new Country(mCountry) : mCountry, country,
+ notifyChange, startLocationBasedDetection);
+ mCountry = country;
+ return mCountry;
+ }
+
+ /**
+ * Run the tasks in the service's thread.
+ */
+ protected void runAfterDetectionAsync(final Country country, final Country detectedCountry,
+ final boolean notifyChange, final boolean startLocationBasedDetection) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ runAfterDetection(
+ country, detectedCountry, notifyChange, startLocationBasedDetection);
+ }
+ });
+ }
+
+ @Override
+ public void setCountryListener(CountryListener listener) {
+ CountryListener prevListener = mListener;
+ mListener = listener;
+ if (mListener == null) {
+ // Stop listening all services
+ removePhoneStateListener();
+ stopLocationBasedDetector();
+ cancelLocationRefresh();
+ } else if (prevListener == null) {
+ addPhoneStateListener();
+ detectCountry(false, true);
+ }
+ }
+
+ void runAfterDetection(final Country country, final Country detectedCountry,
+ final boolean notifyChange, final boolean startLocationBasedDetection) {
+ if (notifyChange) {
+ notifyIfCountryChanged(country, detectedCountry);
+ }
+ if (startLocationBasedDetection && (detectedCountry == null
+ || detectedCountry.getSource() > Country.COUNTRY_SOURCE_LOCATION)
+ && isAirplaneModeOff() && mListener != null && isGeoCoderImplemented()) {
+ // Start finding location when the source is less reliable than the
+ // location and the airplane mode is off (as geocoder will not
+ // work).
+ // TODO : Shall we give up starting the detector within a
+ // period of time?
+ startLocationBasedDetector(mLocationBasedCountryDetectionListener);
+ }
+ if (detectedCountry == null
+ || detectedCountry.getSource() >= Country.COUNTRY_SOURCE_LOCATION) {
+ // Schedule the location refresh if the country source is
+ // not more reliable than the location or no country is
+ // found.
+ // TODO: Listen to the preference change of GPS, Wifi etc,
+ // and start detecting the country.
+ scheduleLocationRefresh();
+ } else {
+ // Cancel the location refresh once the current source is
+ // more reliable than the location.
+ cancelLocationRefresh();
+ stopLocationBasedDetector();
+ }
+ }
+
+ /**
+ * Find the country from LocationProvider.
+ */
+ private synchronized void startLocationBasedDetector(CountryListener listener) {
+ if (mLocationBasedCountryDetector != null) {
+ return;
+ }
+ mLocationBasedCountryDetector = createLocationBasedCountryDetector();
+ mLocationBasedCountryDetector.setCountryListener(listener);
+ mLocationBasedCountryDetector.detectCountry();
+ }
+
+ private synchronized void stopLocationBasedDetector() {
+ if (mLocationBasedCountryDetector != null) {
+ mLocationBasedCountryDetector.stop();
+ mLocationBasedCountryDetector = null;
+ }
+ }
+
+ protected CountryDetectorBase createLocationBasedCountryDetector() {
+ return new LocationBasedCountryDetector(mContext);
+ }
+
+ protected boolean isAirplaneModeOff() {
+ return Settings.System.getInt(
+ mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 0;
+ }
+
+ /**
+ * Notify the country change.
+ */
+ private void notifyIfCountryChanged(final Country country, final Country detectedCountry) {
+ if (detectedCountry != null && mListener != null
+ && (country == null || !country.equals(detectedCountry))) {
+ Slog.d(TAG,
+ "The country was changed from " + country != null ? country.getCountryIso() :
+ country + " to " + detectedCountry.getCountryIso());
+ notifyListener(detectedCountry);
+ }
+ }
+
+ /**
+ * Schedule the next location refresh. We will do nothing if the scheduled task exists.
+ */
+ private synchronized void scheduleLocationRefresh() {
+ if (mLocationRefreshTimer != null) return;
+ mLocationRefreshTimer = new Timer();
+ mLocationRefreshTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ mLocationRefreshTimer = null;
+ detectCountry(false, true);
+ }
+ }, LOCATION_REFRESH_INTERVAL);
+ }
+
+ /**
+ * Cancel the scheduled refresh task if it exists
+ */
+ private synchronized void cancelLocationRefresh() {
+ if (mLocationRefreshTimer != null) {
+ mLocationRefreshTimer.cancel();
+ mLocationRefreshTimer = null;
+ }
+ }
+
+ protected synchronized void addPhoneStateListener() {
+ if (mPhoneStateListener == null && mPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
+ mLastState = null;
+ mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ // TODO: Find out how often we will be notified, if this
+ // method is called too
+ // many times, let's consider querying the network.
+ Slog.d(TAG, "onServiceStateChanged");
+ // We only care the state change
+ if (mLastState == null || mLastState.getState() != serviceState.getState()) {
+ detectCountry(true, true);
+ mLastState = new ServiceState(serviceState);
+ }
+ }
+ };
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ }
+ }
+
+ protected synchronized void removePhoneStateListener() {
+ if (mPhoneStateListener != null) {
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mPhoneStateListener = null;
+ }
+ }
+
+ protected boolean isGeoCoderImplemented() {
+ return Geocoder.isPresent();
+ }
+}
diff --git a/services/java/com/android/server/location/CountryDetectorBase.java b/services/java/com/android/server/location/CountryDetectorBase.java
new file mode 100644
index 0000000..8326ef9
--- /dev/null
+++ b/services/java/com/android/server/location/CountryDetectorBase.java
@@ -0,0 +1,72 @@
+/*
+ * 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.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.os.Handler;
+
+/**
+ * This class defines the methods need to be implemented by the country
+ * detector.
+ * <p>
+ * Calling {@link #detectCountry} to start detecting the country. The country
+ * could be returned immediately if it is available.
+ *
+ * @hide
+ */
+public abstract class CountryDetectorBase {
+ protected final Handler mHandler;
+ protected final Context mContext;
+ protected CountryListener mListener;
+ protected Country mDetectedCountry;
+
+ public CountryDetectorBase(Context ctx) {
+ mContext = ctx;
+ mHandler = new Handler();
+ }
+
+ /**
+ * Start detecting the country that the user is in.
+ *
+ * @return the country if it is available immediately, otherwise null should
+ * be returned.
+ */
+ public abstract Country detectCountry();
+
+ /**
+ * Register a listener to receive the notification when the country is detected or changed.
+ * <p>
+ * The previous listener will be replaced if it exists.
+ */
+ public void setCountryListener(CountryListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Stop detecting the country. The detector should release all system services and be ready to
+ * be freed
+ */
+ public abstract void stop();
+
+ protected void notifyListener(Country country) {
+ if (mListener != null) {
+ mListener.onCountryDetected(country);
+ }
+ }
+}
diff --git a/services/java/com/android/server/location/LocationBasedCountryDetector.java b/services/java/com/android/server/location/LocationBasedCountryDetector.java
new file mode 100755
index 0000000..139f05d
--- /dev/null
+++ b/services/java/com/android/server/location/LocationBasedCountryDetector.java
@@ -0,0 +1,235 @@
+/*
+ * 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 java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.content.Context;
+import android.location.Address;
+import android.location.Country;
+import android.location.Geocoder;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.util.Slog;
+
+/**
+ * This class detects which country the user currently is in through the enabled
+ * location providers and the GeoCoder
+ * <p>
+ * Use {@link #detectCountry} to start querying. If the location can not be
+ * resolved within the given time, the last known location will be used to get
+ * the user country through the GeoCoder. The IllegalStateException will be
+ * thrown if there is a ongoing query.
+ * <p>
+ * The current query can be stopped by {@link #stop()}
+ *
+ * @hide
+ */
+public class LocationBasedCountryDetector extends CountryDetectorBase {
+ private final static String TAG = "LocationBasedCountryDetector";
+ private final static long QUERY_LOCATION_TIMEOUT = 1000 * 60 * 5; // 5 mins
+
+ /**
+ * Used for canceling location query
+ */
+ protected Timer mTimer;
+
+ /**
+ * The thread to query the country from the GeoCoder.
+ */
+ protected Thread mQueryThread;
+ protected List<LocationListener> mLocationListeners;
+
+ private LocationManager mLocationManager;
+ private List<String> mEnabledProviders;
+
+ public LocationBasedCountryDetector(Context ctx) {
+ super(ctx);
+ mLocationManager = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);
+ }
+
+ /**
+ * @return the ISO 3166-1 two letters country code from the location
+ */
+ protected String getCountryFromLocation(Location location) {
+ String country = null;
+ Geocoder geoCoder = new Geocoder(mContext);
+ try {
+ List<Address> addresses = geoCoder.getFromLocation(
+ location.getLatitude(), location.getLongitude(), 1);
+ if (addresses != null && addresses.size() > 0) {
+ country = addresses.get(0).getCountryCode();
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception occurs when getting country from location");
+ }
+ return country;
+ }
+
+ /**
+ * Register the listeners with the location providers
+ */
+ protected void registerEnabledProviders(List<LocationListener> listeners) {
+ int total = listeners.size();
+ for (int i = 0; i< total; i++) {
+ mLocationManager.requestLocationUpdates(
+ mEnabledProviders.get(i), 0, 0, listeners.get(i));
+ }
+ }
+
+ /**
+ * Unregister the listeners with the location providers
+ */
+ protected void unregisterProviders(List<LocationListener> listeners) {
+ for (LocationListener listener : listeners) {
+ mLocationManager.removeUpdates(listener);
+ }
+ }
+
+ /**
+ * @return the last known location from all providers
+ */
+ protected Location getLastKnownLocation() {
+ List<String> providers = mLocationManager.getAllProviders();
+ Location bestLocation = null;
+ for (String provider : providers) {
+ Location lastKnownLocation = mLocationManager.getLastKnownLocation(provider);
+ if (lastKnownLocation != null) {
+ if (bestLocation == null || bestLocation.getTime() < lastKnownLocation.getTime()) {
+ bestLocation = lastKnownLocation;
+ }
+ }
+ }
+ return bestLocation;
+ }
+
+ /**
+ * @return the timeout for querying the location.
+ */
+ protected long getQueryLocationTimeout() {
+ return QUERY_LOCATION_TIMEOUT;
+ }
+
+ /**
+ * @return the total number of enabled location providers
+ */
+ protected int getTotalEnabledProviders() {
+ if (mEnabledProviders == null) {
+ mEnabledProviders = mLocationManager.getProviders(true);
+ }
+ return mEnabledProviders.size();
+ }
+
+ /**
+ * Start detecting the country.
+ * <p>
+ * Queries the location from all location providers, then starts a thread to query the
+ * country from GeoCoder.
+ */
+ @Override
+ public synchronized Country detectCountry() {
+ if (mLocationListeners != null) {
+ throw new IllegalStateException();
+ }
+ // Request the location from all enabled providers.
+ int totalProviders = getTotalEnabledProviders();
+ if (totalProviders > 0) {
+ mLocationListeners = new ArrayList<LocationListener>(totalProviders);
+ for (int i = 0; i < totalProviders; i++) {
+ LocationListener listener = new LocationListener () {
+ public void onLocationChanged(Location location) {
+ if (location != null) {
+ LocationBasedCountryDetector.this.stop();
+ queryCountryCode(location);
+ }
+ }
+ public void onProviderDisabled(String provider) {
+ }
+ public void onProviderEnabled(String provider) {
+ }
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+ mLocationListeners.add(listener);
+ }
+ registerEnabledProviders(mLocationListeners);
+ mTimer = new Timer();
+ mTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ mTimer = null;
+ LocationBasedCountryDetector.this.stop();
+ // Looks like no provider could provide the location, let's try the last
+ // known location.
+ queryCountryCode(getLastKnownLocation());
+ }
+ }, getQueryLocationTimeout());
+ } else {
+ // There is no provider enabled.
+ queryCountryCode(getLastKnownLocation());
+ }
+ return mDetectedCountry;
+ }
+
+ /**
+ * Stop the current query without notifying the listener.
+ */
+ @Override
+ public synchronized void stop() {
+ if (mLocationListeners != null) {
+ unregisterProviders(mLocationListeners);
+ mLocationListeners = null;
+ }
+ if (mTimer != null) {
+ mTimer.cancel();
+ mTimer = null;
+ }
+ }
+
+ /**
+ * Start a new thread to query the country from Geocoder.
+ */
+ private synchronized void queryCountryCode(final Location location) {
+ if (location == null) {
+ notifyListener(null);
+ return;
+ }
+ if (mQueryThread != null) return;
+ mQueryThread = new Thread(new Runnable() {
+ public void run() {
+ String countryIso = null;
+ if (location != null) {
+ countryIso = getCountryFromLocation(location);
+ }
+ if (countryIso != null) {
+ mDetectedCountry = new Country(countryIso, Country.COUNTRY_SOURCE_LOCATION);
+ } else {
+ mDetectedCountry = null;
+ }
+ notifyListener(mDetectedCountry);
+ mQueryThread = null;
+ }
+ });
+ mQueryThread.start();
+ }
+}