diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:43 -0800 |
commit | f013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch) | |
tree | 7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /services | |
parent | e70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff) | |
download | frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.zip frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.gz frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'services')
31 files changed, 4943 insertions, 2325 deletions
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 968467b..1625853 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -73,6 +73,17 @@ class AlarmManagerService extends IAlarmManager.Stub { private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>(); private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>(); + // slots corresponding with the inexact-repeat interval buckets, + // ordered from shortest to longest + private static final long sInexactSlotIntervals[] = { + AlarmManager.INTERVAL_FIFTEEN_MINUTES, + AlarmManager.INTERVAL_HALF_HOUR, + AlarmManager.INTERVAL_HOUR, + AlarmManager.INTERVAL_HALF_DAY, + AlarmManager.INTERVAL_DAY + }; + private long mInexactDeliveryTimes[] = { 0, 0, 0, 0, 0}; + private int mDescriptor; private int mBroadcastRefCount = 0; private PowerManager.WakeLock mWakeLock; @@ -162,6 +173,67 @@ class AlarmManagerService extends IAlarmManager.Stub { } } + public void setInexactRepeating(int type, long triggerAtTime, long interval, + PendingIntent operation) { + if (operation == null) { + Log.w(TAG, "setInexactRepeating ignored because there is no intent"); + return; + } + + // find the slot in the delivery-times array that we will use + int intervalSlot; + for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) { + if (sInexactSlotIntervals[intervalSlot] == interval) { + break; + } + } + + // Non-bucket intervals just fall back to the less-efficient + // unbucketed recurring alarm implementation + if (intervalSlot >= sInexactSlotIntervals.length) { + setRepeating(type, triggerAtTime, interval, operation); + return; + } + + // Align bucketed alarm deliveries by trying to match + // the shortest-interval bucket already scheduled + long bucketTime = 0; + for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) { + if (mInexactDeliveryTimes[slot] > 0) { + bucketTime = mInexactDeliveryTimes[slot]; + break; + } + } + + if (bucketTime == 0) { + // If nothing is scheduled yet, just start at the requested time + bucketTime = triggerAtTime; + } else { + // Align the new alarm with the existing bucketed sequence. To achieve + // alignment, we slide the start time around by min{interval, slot interval} + long adjustment = (interval <= sInexactSlotIntervals[intervalSlot]) + ? interval : sInexactSlotIntervals[intervalSlot]; + + // The bucket may have started in the past; adjust + while (bucketTime < triggerAtTime) { + bucketTime += adjustment; + } + + // Or the bucket may be set to start more than an interval beyond + // our requested trigger time; pull it back to meet our needs + while (bucketTime > triggerAtTime + adjustment) { + bucketTime -= adjustment; + } + } + + // Remember where this bucket started (reducing the amount of later + // fixup required) and set the alarm with the new, bucketed start time. + if (localLOGV) Log.v(TAG, "setInexactRepeating: interval=" + interval + + " bucketTime=" + bucketTime); + mInexactDeliveryTimes[intervalSlot] = bucketTime; + setRepeating(type, bucketTime, interval, operation); + } + public void setTimeZone(String tz) { mContext.enforceCallingOrSelfPermission( "android.permission.SET_TIME_ZONE", @@ -171,15 +243,28 @@ class AlarmManagerService extends IAlarmManager.Stub { TimeZone zone = TimeZone.getTimeZone(tz); // Prevent reentrant calls from stepping on each other when writing // the time zone property + boolean timeZoneWasChanged = false; synchronized (this) { - SystemProperties.set(TIMEZONE_PROPERTY, zone.getID()); + String current = SystemProperties.get(TIMEZONE_PROPERTY); + if (current == null || !current.equals(zone.getID())) { + if (localLOGV) Log.v(TAG, "timezone changed: " + current + ", new=" + zone.getID()); + timeZoneWasChanged = true; + SystemProperties.set(TIMEZONE_PROPERTY, zone.getID()); + + // Update the kernel timezone information + // Kernel tracks time offsets as 'minutes west of GMT' + int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000; + setKernelTimezone(mDescriptor, -(gmtOffset)); + } } - + TimeZone.setDefault(null); - Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED); - intent.putExtra("time-zone", zone.getID()); - mContext.sendBroadcast(intent); + if (timeZoneWasChanged) { + Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + intent.putExtra("time-zone", zone.getID()); + mContext.sendBroadcast(intent); + } } public void remove(PendingIntent operation) { @@ -359,7 +444,8 @@ class AlarmManagerService extends IAlarmManager.Stub { private native void close(int fd); private native void set(int fd, int type, long nanoseconds); private native int waitForAlarm(int fd); - + private native int setKernelTimezone(int fd, int minuteswest); + private void triggerAlarmsLocked(ArrayList<Alarm> alarmList, ArrayList<Alarm> triggerList, long now) @@ -581,6 +667,7 @@ class AlarmManagerService extends IAlarmManager.Stub { public ClockReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_TICK); + filter.addAction(Intent.ACTION_DATE_CHANGED); mContext.registerReceiver(this, filter); } @@ -589,6 +676,14 @@ class AlarmManagerService extends IAlarmManager.Stub { if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) { scheduleTimeTickEvent(); } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) { + // Since the kernel does not keep track of DST, we need to + // reset the TZ information at the beginning of each day + // based off of the current Zone gmt offset + userspace tracked + // daylight savings information. + TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY)); + int gmtOffset = (zone.getRawOffset() + zone.getDSTSavings()) / 60000; + + setKernelTimezone(mDescriptor, -(gmtOffset)); scheduleDateChangedEvent(); } } diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index 608299c..dba8fca 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -17,7 +17,7 @@ package com.android.server; import com.android.internal.app.IBatteryStats; -import com.android.server.am.BatteryStats; +import com.android.server.am.BatteryStatsService; import android.app.ActivityManagerNative; import android.content.Context; @@ -27,7 +27,6 @@ import android.os.BatteryManager; import android.os.Binder; import android.os.RemoteException; import android.os.UEventObserver; -import android.util.Config; import android.util.EventLog; import android.util.Log; @@ -90,9 +89,9 @@ class BatteryService extends Binder { public BatteryService(Context context) { mContext = context; - mBatteryStats = BatteryStats.getService(); + mBatteryStats = BatteryStatsService.getService(); - mUEventObserver.startObserving("DEVPATH=/class/power_supply"); + mUEventObserver.startObserving("SUBSYSTEM=power_supply"); // set initial status update(); @@ -103,6 +102,28 @@ class BatteryService extends Binder { return (mAcOnline || mUsbOnline || mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN); } + final boolean isPowered(int plugTypeSet) { + // assume we are powered if battery state is unknown so + // the "stay on while plugged in" option will work. + if (mBatteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) { + return true; + } + if (plugTypeSet == 0) { + return false; + } + int plugTypeBit = 0; + if (mAcOnline) { + plugTypeBit = BatteryManager.BATTERY_PLUGGED_AC; + } else if (mUsbOnline) { + plugTypeBit = BatteryManager.BATTERY_PLUGGED_USB; + } + return (plugTypeSet & plugTypeBit) != 0; + } + + final int getPlugType() { + return mPlugType; + } + private UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 11f34cc..65e3650 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -35,7 +35,6 @@ import android.os.Message; import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.Settings; -import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Log; @@ -43,7 +42,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; /** - * {@hide} + * @hide */ public class ConnectivityService extends IConnectivityManager.Stub { @@ -59,7 +58,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { * abstractly. */ private NetworkStateTracker mNetTrackers[]; - private boolean mTeardownRequested[]; private WifiStateTracker mWifiStateTracker; private MobileDataStateTracker mMobileDataStateTracker; private WifiWatchdogService mWifiWatchdogService; @@ -69,11 +67,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { private NetworkStateTracker mActiveNetwork; private int mNumDnsEntries; - private static int mDnsChangeCounter; + private static int sDnsChangeCounter; private boolean mTestMode; private static ConnectivityService sServiceInstance; - + private static class ConnectivityThread extends Thread { private Context mContext; @@ -101,7 +99,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { try { // Wait until sServiceInstance has been initialized. thread.wait(); - } catch (InterruptedException e) { + } catch (InterruptedException ignore) { + Log.e(TAG, + "Unexpected InterruptedException while waiting for ConnectivityService thread"); } } } @@ -118,7 +118,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (DBG) Log.v(TAG, "ConnectivityService starting up"); mContext = context; mNetTrackers = new NetworkStateTracker[2]; - mTeardownRequested = new boolean[2]; Handler handler = new MyHandler(); mNetworkPreference = getPersistedNetworkPreference(); @@ -134,7 +133,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { mWifiStateTracker = new WifiStateTracker(context, handler); WifiService wifiService = new WifiService(context, mWifiStateTracker); ServiceManager.addService(Context.WIFI_SERVICE, wifiService); - // The WifiStateTracker should appear first in the list mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker; mMobileDataStateTracker = new MobileDataStateTracker(context, handler); @@ -160,10 +158,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { public synchronized void setNetworkPreference(int preference) { enforceChangePermission(); if (ConnectivityManager.isNetworkTypeValid(preference)) { - int oldPreference = mNetworkPreference; - persistNetworkPreference(preference); - if (mNetworkPreference != oldPreference) + if (mNetworkPreference != preference) { + persistNetworkPreference(preference); + mNetworkPreference = preference; enforcePreference(); + } } } @@ -174,14 +173,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void persistNetworkPreference(int networkPreference) { final ContentResolver cr = mContext.getContentResolver(); - Settings.System.putInt(cr, Settings.System.NETWORK_PREFERENCE, networkPreference); + Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, networkPreference); } private int getPersistedNetworkPreference() { final ContentResolver cr = mContext.getContentResolver(); - final int networkPrefSetting = Settings.System - .getInt(cr, Settings.System.NETWORK_PREFERENCE, -1); + final int networkPrefSetting = Settings.Secure + .getInt(cr, Settings.Secure.NETWORK_PREFERENCE, -1); if (networkPrefSetting != -1) { return networkPrefSetting; } @@ -219,7 +218,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private boolean teardown(NetworkStateTracker netTracker) { if (netTracker.teardown()) { - mTeardownRequested[netTracker.getNetworkInfo().getType()] = true; + netTracker.setTeardownRequested(true); return true; } else { return false; @@ -227,9 +226,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { } /** - * Return NetworkInfo for the active network interface. It is assumed that at most - * one network is active at a time. If more than one is active, it is indeterminate - * which will be returned. + * Return NetworkInfo for the active (i.e., connected) network interface. + * It is assumed that at most one network is active at a time. If more + * than one is active, it is indeterminate which will be returned. * @return the info for the active network, or {@code null} if none is active */ public NetworkInfo getActiveNetworkInfo() { @@ -291,7 +290,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid()); } return -1; - } public int stopUsingNetworkFeature(int networkType, String feature) { @@ -339,8 +337,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { int numConnectedNets = 0; for (NetworkStateTracker nt : mNetTrackers) { - if (nt.getNetworkInfo().isConnected() - && !mTeardownRequested[nt.getNetworkInfo().getType()]) { + if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) { ++numConnectedNets; } } @@ -369,13 +366,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName()); + mNetTrackers[info.getType()].setTeardownRequested(false); /* * If the disconnected network is not the active one, then don't report * this as a loss of connectivity. What probably happened is that we're * getting the disconnect for a network that we explicitly disabled * in accordance with network preference policies. */ - mTeardownRequested[info.getType()] = false; if (mActiveNetwork == null || info.getType() != mActiveNetwork.getNetworkInfo().getType()) return; @@ -386,13 +383,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { newNet = mMobileDataStateTracker; } + /** + * 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. + */ NetworkInfo switchTo = null; if (newNet.isAvailable()) { mActiveNetwork = newNet; switchTo = newNet.getNetworkInfo(); switchTo.setFailover(true); - if (!switchTo.isConnectedOrConnecting()) + if (!switchTo.isConnectedOrConnecting()) { newNet.reconnect(); + } + } else { + newNet.reconnect(); } boolean otherNetworkConnected = false; @@ -449,8 +456,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { mContext.sendStickyBroadcast(intent); } + /** + * Called when an attempt to fail over to another network has failed. + * @param info the {@link NetworkInfo} for the failed network + */ private void handleConnectionFailure(NetworkInfo info) { - mTeardownRequested[info.getType()] = false; + mNetTrackers[info.getType()].setTeardownRequested(false); if (getActiveNetworkInfo() == null) { String reason = info.getReason(); String extraInfo = info.getExtraInfo(); @@ -496,10 +507,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { otherNet = mMobileDataStateTracker; } /* - * Check policy to see whether we are now connected to a network that - * takes precedence over the other one. If so, we need to tear down - * the other one. - */ + * Check policy to see whether we are connected to a non-preferred + * network that now needs to be torn down. + */ NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo(); NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo(); if (wifiInfo.isConnected() && mobileInfo.isConnected()) { @@ -510,7 +520,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } boolean toredown = false; - mTeardownRequested[info.getType()] = false; + thisNet.setTeardownRequested(false); if (!mTestMode && deadnet != null) { if (DBG) Log.v(TAG, "Policy requires " + deadnet.getNetworkInfo().getTypeName() + " teardown"); @@ -520,6 +530,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + /* + * Note that if toredown is true, deadnet cannot be null, so there is + * no danger of a null pointer exception here.. + */ if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) { mActiveNetwork = thisNet; if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + info.getTypeName()); @@ -592,10 +606,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI; int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue; - for (int net = ConnectivityManager.TYPE_WIFI; net != stopValue; net += incrValue) { - NetworkStateTracker nt = mNetTrackers[net]; - if (nt.getNetworkInfo().isConnected() - && !mTeardownRequested[nt.getNetworkInfo().getType()]) { + for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; netType += incrValue) { + NetworkStateTracker nt = mNetTrackers[netType]; + if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) { ++numConnectedNets; String[] dnsList = nt.getNameServers(); for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) { @@ -613,7 +626,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } mNumDnsEntries = index - 1; // Notify the name resolver library of the change - SystemProperties.set("net.dnschange", String.valueOf(mDnsChangeCounter++)); + SystemProperties.set("net.dnschange", String.valueOf(sDnsChangeCounter++)); return numConnectedNets; } @@ -650,13 +663,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { info.getState() + "/" + info.getDetailedState()); // Connectivity state changed: - // [31-11] Reserved for future use - // [10-9] Mobile network connection type (as defined by the TelephonyManager) - // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState) + // [31-13] Reserved for future use + // [12-9] Network subtype (for mobile network, as defined by TelephonyManager) + // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState) // [2-0] Network type (as defined by ConnectivityManager) int eventLogParam = (info.getType() & 0x7) | - ((info.getDetailedState().ordinal() & 0x3f) << 3) | - (TelephonyManager.getDefault().getNetworkType() << 9); + ((info.getDetailedState().ordinal() & 0x3f) << 3) | + (info.getSubtype() << 9); EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam); if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) { @@ -687,6 +700,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: handleConfigurationChange(); break; + + case NetworkStateTracker.EVENT_ROAMING_CHANGED: + // fill me in + break; + + case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: + // fill me in + break; } } } diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java new file mode 100644 index 0000000..04b1900 --- /dev/null +++ b/services/java/com/android/server/DeviceStorageMonitorService.java @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2007-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 com.android.server.am.ActivityManagerService; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.IPackageDataObserver; +import android.content.pm.IPackageManager; +import android.os.Binder; +import android.os.Handler; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.StatFs; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.provider.Settings.Gservices; +import android.util.Config; +import android.util.EventLog; +import android.util.Log; +import android.provider.Settings; + +/** + * This class implements a service to monitor the amount of disk storage space + * on the device. If the free storage on device is less than a tunable threshold value + * (default is 10%. this value is a gservices parameter) a low memory notification is + * displayed to alert the user. If the user clicks on the low memory notification the + * Application Manager application gets launched to let the user free storage space. + * Event log events: + * A low memory event with the free storage on device in bytes is logged to the event log + * when the device goes low on storage space. + * The amount of free storage on the device is periodically logged to the event log. The log + * interval is a gservices parameter with a default value of 12 hours + * When the free storage differential goes below a threshold(again a gservices parameter with + * a default value of 2MB), the free memory is logged to the event log + */ +class DeviceStorageMonitorService extends Binder { + private static final String TAG = "DeviceStorageMonitorService"; + private static final boolean DEBUG = false; + private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; + private static final int DEVICE_MEMORY_WHAT = 1; + 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_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes + private static final int EVENT_LOG_STORAGE_BELOW_THRESHOLD = 2744; + private static final int EVENT_LOG_LOW_STORAGE_NOTIFICATION = 2745; + private static final int EVENT_LOG_FREE_STORAGE_LEFT = 2746; + private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB + private long mFreeMem; + private long mLastReportedFreeMem; + private long mLastReportedFreeMemTime; + private boolean mLowMemFlag=false; + private Context mContext; + private ContentResolver mContentResolver; + int mBlkSize; + long mTotalMemory; + StatFs mFileStats; + private static final String DATA_PATH="/data"; + long mThreadStartTime = -1; + boolean mClearSucceeded = false; + boolean mClearingCache; + private Intent mStorageLowIntent; + private Intent mStorageOkIntent; + + /** + * This string is used for ServiceManager access to this class. + */ + static final String SERVICE = "devicestoragemonitor"; + + /** + * Handler that checks the amount of disk space on the device and sends a + * notification if the device runs low on disk space + */ + Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + //dont handle an invalid message + if (msg.what != DEVICE_MEMORY_WHAT) { + Log.e(TAG, "Will not process invalid message"); + return; + } + checkMemory(); + } + }; + + class CachePackageDataObserver extends IPackageDataObserver.Stub { + public void onRemoveCompleted(String packageName, boolean succeeded) { + mClearSucceeded = succeeded; + mClearingCache = false; + if(localLOGV) Log.i(TAG, " Clear succeeded:"+mClearSucceeded + +", mClearingCache:"+mClearingCache); + } + } + + private final void restatDataDir() { + mFileStats.restat(DATA_PATH); + mFreeMem = mFileStats.getAvailableBlocks()*mBlkSize; + // Allow freemem to be overridden by debug.freemem for testing + String debugFreeMem = SystemProperties.get("debug.freemem"); + if (!"".equals(debugFreeMem)) { + mFreeMem = Long.parseLong(debugFreeMem); + } + // Read the log interval from Gservices + long freeMemLogInterval = Gservices.getLong(mContentResolver, + Gservices.SYS_FREE_STORAGE_LOG_INTERVAL, + DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000; + //log the amount of free memory in event log + long currTime = SystemClock.elapsedRealtime(); + if((mLastReportedFreeMemTime == 0) || + (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) { + mLastReportedFreeMemTime = currTime; + EventLog.writeEvent(EVENT_LOG_FREE_STORAGE_LEFT, mFreeMem); + } + // Read the reporting threshold from Gservices + long threshold = Gservices.getLong(mContentResolver, + Gservices.DISK_FREE_CHANGE_REPORTING_THRESHOLD, + DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD); + // If mFree changed significantly log the new value + long delta = mFreeMem - mLastReportedFreeMem; + if (delta > threshold || delta < -threshold) { + mLastReportedFreeMem = mFreeMem; + EventLog.writeEvent(EVENT_LOG_STORAGE_BELOW_THRESHOLD, mFreeMem); + } + } + + private final void clearCache() { + CachePackageDataObserver observer = new CachePackageDataObserver(); + mClearingCache = true; + try { + IPackageManager.Stub.asInterface(ServiceManager.getService("package")). + freeApplicationCache(getMemThreshold(), observer); + } catch (RemoteException e) { + Log.w(TAG, "Failed to get handle for PackageManger Exception: "+e); + mClearingCache = false; + mClearSucceeded = false; + } + } + + private final void checkMemory() { + //if the thread that was started to clear cache is still running do nothing till its + //finished clearing cache. Ideally this flag could be modified by clearCache + // and should be accessed via a lock but even if it does this test will fail now and + //hopefully the next time this flag will be set to the correct value. + if(mClearingCache) { + if(localLOGV) Log.i(TAG, "Thread already running just skip"); + //make sure the thread is not hung for too long + long diffTime = System.currentTimeMillis() - mThreadStartTime; + if(diffTime > (10*60*1000)) { + Log.w(TAG, "Thread that clears cache file seems to run for ever"); + } + } else { + restatDataDir(); + if (localLOGV) Log.v(TAG, "freeMemory="+mFreeMem); + //post intent to NotificationManager to display icon if necessary + long memThreshold = getMemThreshold(); + if (mFreeMem < memThreshold) { + if (!mLowMemFlag) { + //see if clearing cache helps + mThreadStartTime = System.currentTimeMillis(); + clearCache(); + Log.i(TAG, "Running low on memory. Sending notification"); + sendNotification(); + mLowMemFlag = true; + } else { + if (localLOGV) Log.v(TAG, "Running low on memory " + + "notification already sent. do nothing"); + } + } else { + if (mLowMemFlag) { + Log.i(TAG, "Memory available. Cancelling notification"); + cancelNotification(); + mLowMemFlag = false; + } + } + } + if(localLOGV) Log.i(TAG, "Posting Message again"); + //keep posting messages to itself periodically + mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT), + MONITOR_INTERVAL*60*1000); + } + + /* + * just query settings to retrieve the memory threshold. + * Preferred this over using a ContentObserver since Settings.Gservices caches the value + * any way + */ + private long getMemThreshold() { + int value = Settings.Gservices.getInt( + mContentResolver, + Settings.Gservices.SYS_STORAGE_THRESHOLD_PERCENTAGE, + DEFAULT_THRESHOLD_PERCENTAGE); + if(localLOGV) Log.v(TAG, "Threshold Percentage="+value); + //evaluate threshold value + return mTotalMemory*value; + } + + /** + * Constructor to run service. initializes the disk space threshold value + * and posts an empty message to kickstart the process. + */ + public DeviceStorageMonitorService(Context context) { + mLastReportedFreeMemTime = 0; + mContext = context; + mContentResolver = mContext.getContentResolver(); + //create StatFs object + mFileStats = new StatFs(DATA_PATH); + //initialize block size + mBlkSize = mFileStats.getBlockSize(); + //initialize total storage on device + mTotalMemory = (mFileStats.getBlockCount()*mBlkSize)/100; + mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW); + mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK); + checkMemory(); + } + + + /** + * This method sends a notification to NotificationManager to display + * an error dialog indicating low disk space and launch the Installer + * application + */ + private final void sendNotification() { + if(localLOGV) Log.i(TAG, "Sending low memory notification"); + //log the event to event log with the amount of free storage(in bytes) left on the device + EventLog.writeEvent(EVENT_LOG_LOW_STORAGE_NOTIFICATION, mFreeMem); + // Pack up the values and broadcast them to everyone + Intent lowMemIntent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE); + lowMemIntent.putExtra("memory", mFreeMem); + lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + NotificationManager mNotificationMgr = + (NotificationManager)mContext.getSystemService( + Context.NOTIFICATION_SERVICE); + CharSequence title = mContext.getText( + com.android.internal.R.string.low_internal_storage_view_title); + CharSequence details = mContext.getText( + com.android.internal.R.string.low_internal_storage_view_text); + PendingIntent intent = PendingIntent.getActivity(mContext, 0, lowMemIntent, 0); + Notification notification = new Notification(); + notification.icon = com.android.internal.R.drawable.stat_notify_disk_full; + notification.tickerText = title; + notification.flags |= Notification.FLAG_NO_CLEAR; + notification.setLatestEventInfo(mContext, title, details, intent); + mNotificationMgr.notify(LOW_MEMORY_NOTIFICATION_ID, notification); + mContext.sendStickyBroadcast(mStorageLowIntent); + } + + /** + * Cancels low storage notification and sends OK intent. + */ + private final void cancelNotification() { + if(localLOGV) Log.i(TAG, "Canceling low memory notification"); + NotificationManager mNotificationMgr = + (NotificationManager)mContext.getSystemService( + Context.NOTIFICATION_SERVICE); + //cancel notification since memory has been freed + mNotificationMgr.cancel(LOW_MEMORY_NOTIFICATION_ID); + + mContext.removeStickyBroadcast(mStorageLowIntent); + mContext.sendBroadcast(mStorageOkIntent); + } + + public void updateMemory() { + ActivityManagerService ams = (ActivityManagerService)ServiceManager.getService("activity"); + int callingUid = getCallingUid(); + if(callingUid != Process.SYSTEM_UID) { + return; + } + //remove queued messages + mHandler.removeMessages(DEVICE_MEMORY_WHAT); + //force an early check + checkMemory(); + } +} diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java index 9f7701d..cb681e0 100644 --- a/services/java/com/android/server/HeadsetObserver.java +++ b/services/java/com/android/server/HeadsetObserver.java @@ -32,7 +32,7 @@ import java.io.FileNotFoundException; class HeadsetObserver extends UEventObserver { private static final String TAG = HeadsetObserver.class.getSimpleName(); - private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/class/switch/h2w"; + 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"; diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java index 70bf38e..7b8a2a4 100644 --- a/services/java/com/android/server/InputDevice.java +++ b/services/java/com/android/server/InputDevice.java @@ -75,6 +75,7 @@ public class InputDevice { float temp; float scaledPressure = 1.0f; float scaledSize = 0; + int edgeFlags = 0; if (isAbs) { int w = display.getWidth()-1; int h = display.getHeight()-1; @@ -118,6 +119,19 @@ public class InputDevice { scaledY = temp; break; } + + if (scaledX == 0) { + edgeFlags += MotionEvent.EDGE_LEFT; + } else if (scaledX == display.getWidth() - 1.0f) { + edgeFlags += MotionEvent.EDGE_RIGHT; + } + + if (scaledY == 0) { + edgeFlags += MotionEvent.EDGE_TOP; + } else if (scaledY == display.getHeight() - 1.0f) { + edgeFlags += MotionEvent.EDGE_BOTTOM; + } + } else { scaledX *= xMoveScale; scaledY *= yMoveScale; @@ -138,19 +152,6 @@ public class InputDevice { break; } } - - int edgeFlags = 0; - if (scaledX == 0) { - edgeFlags += MotionEvent.EDGE_LEFT; - } else if (scaledX == display.getWidth() - 1.0f) { - edgeFlags += MotionEvent.EDGE_RIGHT; - } - - if (scaledY == 0) { - edgeFlags += MotionEvent.EDGE_TOP; - } else if (scaledY == display.getHeight() - 1.0f) { - edgeFlags += MotionEvent.EDGE_BOTTOM; - } changed = false; if (down != lastDown) { @@ -171,6 +172,8 @@ public class InputDevice { xPrecision, yPrecision, device.id, edgeFlags); } else { if (currentMove != null) { + if (false) Log.i("InputDevice", "Adding batch x=" + scaledX + + " y=" + scaledY + " to " + currentMove); currentMove.addBatch(curTime, scaledX, scaledY, scaledPressure, scaledSize, metaState); if (WindowManagerPolicy.WATCH_POINTER) { diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java new file mode 100644 index 0000000..c701ca1 --- /dev/null +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -0,0 +1,1305 @@ +/* + * Copyright (C) 2006-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 com.android.internal.os.HandlerCaller; +import com.android.internal.view.IInputContext; +import com.android.internal.view.IInputMethod; +import com.android.internal.view.IInputMethodCallback; +import com.android.internal.view.IInputMethodClient; +import com.android.internal.view.IInputMethodManager; +import com.android.internal.view.IInputMethodSession; +import com.android.internal.view.InputBindResult; + +import com.android.server.status.IconData; +import com.android.server.status.StatusBarService; + +import org.xmlpull.v1.XmlPullParserException; + +import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.IntentFilter; +import android.content.DialogInterface.OnCancelListener; +import android.content.Intent; +import android.content.ServiceConnection; +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.database.ContentObserver; +import android.net.Uri; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Message; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; +import android.util.PrintWriterPrinter; +import android.util.Printer; +import android.view.IWindowManager; +import android.view.WindowManager; +import android.view.inputmethod.DefaultInputMethod; +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 java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This class provides a system service that manages input methods. + */ +public class InputMethodManagerService extends IInputMethodManager.Stub + implements ServiceConnection, Handler.Callback { + static final boolean DEBUG = false; + static final String TAG = "InputManagerService"; + + static final int MSG_SHOW_IM_PICKER = 1; + + static final int MSG_UNBIND_INPUT = 1000; + static final int MSG_BIND_INPUT = 1010; + static final int MSG_SHOW_SOFT_INPUT = 1020; + static final int MSG_HIDE_SOFT_INPUT = 1030; + static final int MSG_ATTACH_TOKEN = 1040; + static final int MSG_CREATE_SESSION = 1050; + + static final int MSG_START_INPUT = 2000; + static final int MSG_RESTART_INPUT = 2010; + + static final int MSG_UNBIND_METHOD = 3000; + static final int MSG_BIND_METHOD = 3010; + + final Context mContext; + final Handler mHandler; + final SettingsObserver mSettingsObserver; + final StatusBarService mStatusBar; + final IBinder mInputMethodIcon; + final IconData mInputMethodData; + final IWindowManager mIWindowManager; + final HandlerCaller mCaller; + + final InputBindResult mNoBinding = new InputBindResult(null, null, -1); + + // 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(':'); + + class SessionState { + final ClientState client; + final IInputMethod method; + final IInputMethodSession session; + + @Override + public String toString() { + return "SessionState{uid " + client.uid + " pid " + client.pid + + " method " + Integer.toHexString( + System.identityHashCode(method)) + + " session " + Integer.toHexString( + System.identityHashCode(session)) + + "}"; + } + + SessionState(ClientState _client, IInputMethod _method, + IInputMethodSession _session) { + client = _client; + method = _method; + session = _session; + } + } + + class ClientState { + final IInputMethodClient client; + final IInputContext inputContext; + final int uid; + final int pid; + final InputBinding binding; + + boolean sessionRequested; + SessionState curSession; + + @Override + public String toString() { + return "ClientState{" + Integer.toHexString( + System.identityHashCode(this)) + " uid " + uid + + " pid " + pid + "}"; + } + + ClientState(IInputMethodClient _client, IInputContext _inputContext, + int _uid, int _pid) { + client = _client; + inputContext = _inputContext; + uid = _uid; + pid = _pid; + binding = new InputBinding(null, inputContext.asBinder(), uid, pid); + } + } + + final HashMap<IBinder, ClientState> mClients + = new HashMap<IBinder, ClientState>(); + + /** + * Id of the currently selected input method. + */ + String mCurMethodId; + + /** + * The current binding sequence number, incremented every time there is + * a new bind performed. + */ + int mCurSeq; + + /** + * The client that is currently bound to an input method. + */ + ClientState mCurClient; + + /** + * The attributes last provided by the current client. + */ + EditorInfo mCurAttribute; + + /** + * The input method ID of the input method service that we are currently + * connected to or in the process of connecting to. + */ + String mCurId; + + /** + * Set to true if our ServiceConnection is currently actively bound to + * a service (whether or not we have gotten its IBinder back yet). + */ + boolean mHaveConnection; + + /** + * Set if the client has asked for the input method to be shown. + */ + boolean mShowRequested; + + /** + * Set if we last told the input method to show itself. + */ + boolean mInputShown; + + /** + * The Intent used to connect to the current input method. + */ + Intent mCurIntent; + + /** + * The token we have made for the currently active input method, to + * identify it in the future. + */ + IBinder mCurToken; + + /** + * If non-null, this is the input method service we are currently connected + * to. + */ + IInputMethod mCurMethod; + + /** + * Have we called mCurMethod.bindInput()? + */ + boolean mBoundToMethod; + + /** + * Currently enabled session. Only touched by service thread, not + * protected by a lock. + */ + SessionState mEnabledSession; + + /** + * True if the screen is on. The value is true initially. + */ + boolean mScreenOn = true; + + AlertDialog.Builder mDialogBuilder; + AlertDialog mSwitchingDialog; + InputMethodInfo[] mIms; + CharSequence[] mItems; + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.DEFAULT_INPUT_METHOD), false, this); + } + + @Override public void onChange(boolean selfChange) { + synchronized (mMethodMap) { + updateFromSettingsLocked(); + } + } + } + + class ScreenOnOffReceiver extends android.content.BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { + mScreenOn = true; + } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + mScreenOn = false; + } else { + Log.e(TAG, "Unexpected intent " + intent); + } + + // Inform the current client of the change in active status + try { + if (mCurClient != null && mCurClient.client != null) { + mCurClient.client.setActive(mScreenOn); + } + } catch (RemoteException e) { + Log.e(TAG, "Got RemoteException sending 'screen on/off' notification", e); + } + } + } + + class PackageReceiver extends android.content.BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mMethodMap) { + buildInputMethodListLocked(mMethodList, mMethodMap); + + InputMethodInfo curIm = null; + String curInputMethodId = Settings.Secure.getString(context + .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + final int N = mMethodList.size(); + if (curInputMethodId != null) { + for (int i=0; i<N; i++) { + if (mMethodList.get(i).getId().equals(curInputMethodId)) { + curIm = mMethodList.get(i); + } + } + } + + boolean changed = false; + + Uri uri = intent.getData(); + String pkg = uri != null ? uri.getSchemeSpecificPart() : null; + if (curIm != null && curIm.getPackageName().equals(pkg)) { + ServiceInfo si = null; + try { + si = mContext.getPackageManager().getServiceInfo( + curIm.getComponent(), 0); + } catch (PackageManager.NameNotFoundException ex) { + } + if (si == null) { + // Uh oh, current input method is no longer around! + // Pick another one... + Log.i(TAG, "Current input method removed: " + curInputMethodId); + List<InputMethodInfo> enabled = getEnabledInputMethodListLocked(); + if (enabled != null && enabled.size() > 0) { + changed = true; + curIm = enabled.get(0); + curInputMethodId = curIm.getId(); + Log.i(TAG, "Switching to: " + curInputMethodId); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, + curInputMethodId); + } else if (curIm != null) { + changed = true; + curIm = null; + curInputMethodId = ""; + Log.i(TAG, "Unsetting current input method"); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, + curInputMethodId); + } + } + + } else if (curIm == null) { + // We currently don't have a default input method... is + // one now available? + List<InputMethodInfo> enabled = getEnabledInputMethodListLocked(); + if (enabled != null && enabled.size() > 0) { + changed = true; + curIm = enabled.get(0); + curInputMethodId = curIm.getId(); + Log.i(TAG, "New default input method: " + curInputMethodId); + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, + curInputMethodId); + } + } + + if (changed) { + updateFromSettingsLocked(); + } + } + } + } + + class MethodCallback extends IInputMethodCallback.Stub { + final IInputMethod mMethod; + + MethodCallback(IInputMethod method) { + mMethod = method; + } + + public void finishedEvent(int seq, boolean handled) throws RemoteException { + } + + public void sessionCreated(IInputMethodSession session) throws RemoteException { + onSessionCreated(mMethod, session); + } + } + + public InputMethodManagerService(Context context, StatusBarService statusBar) { + mContext = context; + mHandler = new Handler(this); + mIWindowManager = IWindowManager.Stub.asInterface( + ServiceManager.getService(Context.WINDOW_SERVICE)); + mCaller = new HandlerCaller(context, new HandlerCaller.Callback() { + public void executeMessage(Message msg) { + handleMessage(msg); + } + }); + + IntentFilter packageFilt = new IntentFilter(); + packageFilt.addAction(Intent.ACTION_PACKAGE_ADDED); + packageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED); + packageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED); + packageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED); + packageFilt.addDataScheme("package"); + mContext.registerReceiver(new PackageReceiver(), packageFilt); + + IntentFilter screenOnOffFilt = new IntentFilter(); + screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON); + screenOnOffFilt.addAction(Intent.ACTION_SCREEN_OFF); + mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt); + + buildInputMethodListLocked(mMethodList, mMethodMap); + + final String enabledStr = Settings.Secure.getString( + mContext.getContentResolver(), + Settings.Secure.ENABLED_INPUT_METHODS); + Log.i(TAG, "Enabled input methods: " + enabledStr); + if (enabledStr == null) { + Log.i(TAG, "Enabled input methods has not been set, enabling all"); + 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); + Log.i(TAG, "Adding: " + imi.getId()); + if (i > 0) sb.append(':'); + sb.append(imi.getId()); + if (defIm == null && imi.getIsDefaultResourceId() != 0) { + try { + Resources res = mContext.createPackageContext( + imi.getPackageName(), 0).getResources(); + if (res.getBoolean(imi.getIsDefaultResourceId())) { + defIm = imi; + Log.i(TAG, "Selected default: " + imi.getId()); + } + } catch (PackageManager.NameNotFoundException ex) { + } catch (Resources.NotFoundException ex) { + } + } + } + if (defIm == null && N > 0) { + defIm = mMethodList.get(0); + Log.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()); + } + } + + mStatusBar = statusBar; + mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0); + mInputMethodIcon = statusBar.addIcon(mInputMethodData, null); + statusBar.setIconVisibility(mInputMethodIcon, false); + + mSettingsObserver = new SettingsObserver(mHandler); + updateFromSettingsLocked(); + } + + @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) { + // The input method manager only throws security exceptions, so let's + // log all others. + if (!(e instanceof SecurityException)) { + Log.e(TAG, "Input Method Manager Crash", e); + } + throw e; + } + } + + public void systemReady() { + + } + + public List<InputMethodInfo> getInputMethodList() { + synchronized (mMethodMap) { + return new ArrayList<InputMethodInfo>(mMethodList); + } + } + + public List<InputMethodInfo> getEnabledInputMethodList() { + synchronized (mMethodMap) { + return 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); + + while (splitter.hasNext()) { + InputMethodInfo info = mMethodMap.get(splitter.next()); + if (info != null) { + res.add(info); + } + } + } + + return res; + } + + public void addClient(IInputMethodClient client, + IInputContext inputContext, int uid, int pid) { + synchronized (mMethodMap) { + mClients.put(client.asBinder(), new ClientState(client, + inputContext, uid, pid)); + } + } + + public void removeClient(IInputMethodClient client) { + synchronized (mMethodMap) { + mClients.remove(client.asBinder()); + } + } + + void executeOrSendMessage(IInterface target, Message msg) { + if (target.asBinder() instanceof Binder) { + mCaller.sendMessage(msg); + } else { + handleMessage(msg); + msg.recycle(); + } + } + + void unbindCurrentInputLocked() { + if (mCurClient != null) { + if (DEBUG) Log.v(TAG, "unbindCurrentInputLocked: client = " + + mCurClient.client.asBinder()); + if (mBoundToMethod) { + mBoundToMethod = false; + if (mCurMethod != null) { + executeOrSendMessage(mCurMethod, mCaller.obtainMessageO( + MSG_UNBIND_INPUT, mCurMethod)); + } + } + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( + MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); + mCurClient.sessionRequested = false; + + // Call setActive(false) on the old client + try { + mCurClient.client.setActive(false); + } catch (RemoteException e) { + Log.e(TAG, "Got RemoteException sending setActive(false) notification", e); + } + mCurClient = null; + } + } + + InputBindResult attachNewInputLocked(boolean initial, boolean needResult) { + if (!mBoundToMethod) { + executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( + MSG_BIND_INPUT, mCurMethod, mCurClient.binding)); + mBoundToMethod = true; + } + final SessionState session = mCurClient.curSession; + if (initial) { + executeOrSendMessage(session.method, mCaller.obtainMessageOO( + MSG_START_INPUT, session, mCurAttribute)); + } else { + executeOrSendMessage(session.method, mCaller.obtainMessageOO( + MSG_RESTART_INPUT, session, mCurAttribute)); + } + if (mShowRequested) { + showCurrentInputLocked(); + } + return needResult + ? new InputBindResult(session.session, mCurId, mCurSeq) + : null; + } + + InputBindResult startInputLocked(IInputMethodClient client, + EditorInfo attribute, boolean initial, boolean needResult) { + // If no method is currently selected, do nothing. + if (mCurMethodId == null) { + return mNoBinding; + } + + ClientState cs = mClients.get(client.asBinder()); + if (cs == null) { + throw new IllegalArgumentException("unknown client " + + client.asBinder()); + } + + try { + if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) { + // Check with the window manager to make sure this client actually + // has a window with focus. If not, reject. This is thread safe + // because if the focus changes some time before or after, the + // next client receiving focus that has any interest in input will + // be calling through here after that change happens. + Log.w(TAG, "Starting input on non-focused client " + cs.client + + " (uid=" + cs.uid + " pid=" + cs.pid + ")"); + return null; + } + } catch (RemoteException e) { + } + + if (mCurClient != cs) { + // If the client is changing, we need to switch over to the new + // one. + unbindCurrentInputLocked(); + if (DEBUG) Log.v(TAG, "switching to client: client = " + + cs.client.asBinder()); + + // If the screen is on, inform the new client it is active + if (mScreenOn) { + try { + cs.client.setActive(mScreenOn); + } catch (RemoteException e) { + Log.e(TAG, "Got RemoteException sending setActive notification", e); + } + } + } + + // Bump up the sequence for this client and attach it. + mCurSeq++; + if (mCurSeq <= 0) mCurSeq = 1; + mCurClient = cs; + mCurAttribute = attribute; + + // Check if the input method is changing. + if (mCurId != null && mCurId.equals(mCurMethodId)) { + if (cs.curSession != null) { + // Fast case: if we are already connected to the input method, + // then just return it. + return attachNewInputLocked(initial, needResult); + } + if (mHaveConnection) { + if (mCurMethod != null && !cs.sessionRequested) { + cs.sessionRequested = true; + if (DEBUG) Log.v(TAG, "Creating new session for client " + cs); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( + MSG_CREATE_SESSION, mCurMethod, + new MethodCallback(mCurMethod))); + } + return new InputBindResult(null, mCurId, mCurSeq); + } + } + + InputMethodInfo info = mMethodMap.get(mCurMethodId); + if (info == null) { + throw new IllegalArgumentException("Unknown id: " + mCurMethodId); + } + + if (mCurToken != null) { + try { + mIWindowManager.removeWindowToken(mCurToken); + } catch (RemoteException e) { + } + mCurToken = null; + } + + if (mHaveConnection) { + mContext.unbindService(this); + mHaveConnection = false; + } + + clearCurMethod(); + + mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE); + mCurIntent.setComponent(info.getComponent()); + if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) { + mHaveConnection = true; + mCurId = info.getId(); + mCurToken = new Binder(); + try { + mIWindowManager.addWindowToken(mCurToken, + WindowManager.LayoutParams.TYPE_INPUT_METHOD); + } catch (RemoteException e) { + } + return new InputBindResult(null, mCurId, mCurSeq); + } else { + mCurIntent = null; + Log.e(TAG, "Failure connecting to input method service: " + + mCurIntent); + } + return null; + } + + public InputBindResult startInput(IInputMethodClient client, + EditorInfo attribute, boolean initial, boolean needResult) { + synchronized (mMethodMap) { + final long ident = Binder.clearCallingIdentity(); + try { + return startInputLocked(client, attribute, initial, needResult); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + public void finishInput(IInputMethodClient client) { + } + + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mMethodMap) { + if (mCurIntent != null && name.equals(mCurIntent.getComponent())) { + mCurMethod = IInputMethod.Stub.asInterface(service); + if (mCurClient != null) { + executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( + MSG_ATTACH_TOKEN, mCurMethod, mCurToken)); + if (mCurClient != null) { + if (DEBUG) Log.v(TAG, "Creating first session while with client " + + mCurClient); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( + MSG_CREATE_SESSION, mCurMethod, + new MethodCallback(mCurMethod))); + } + } + } + } + } + + void onSessionCreated(IInputMethod method, IInputMethodSession session) { + synchronized (mMethodMap) { + if (mCurMethod == method) { + if (mCurClient != null) { + mCurClient.curSession = new SessionState(mCurClient, + method, session); + mCurClient.sessionRequested = false; + InputBindResult res = attachNewInputLocked(true, true); + if (res.method != null) { + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO( + MSG_BIND_METHOD, mCurClient.client, res)); + } + } + } + } + } + + void clearCurMethod() { + if (mCurMethod != null) { + for (ClientState cs : mClients.values()) { + cs.sessionRequested = false; + cs.curSession = null; + } + mCurMethod = null; + } + } + + public void onServiceDisconnected(ComponentName name) { + synchronized (mMethodMap) { + if (DEBUG) Log.v(TAG, "Service disconnected: " + name + + " mCurIntent=" + mCurIntent); + if (mCurMethod != null && mCurIntent != null + && name.equals(mCurIntent.getComponent())) { + clearCurMethod(); + mShowRequested = mInputShown; + mInputShown = false; + if (mCurClient != null) { + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( + MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); + } + } + } + } + + public void updateStatusIcon(int iconId, String iconPackage) { + if (iconId == 0) { + Log.d(TAG, "hide the small icon for the input method"); + mStatusBar.setIconVisibility(mInputMethodIcon, false); + } else { + Log.d(TAG, "show a small icon for the input method"); + + if (iconPackage != null + && iconPackage + .equals(InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) { + iconPackage = null; + } + + mInputMethodData.iconId = iconId; + mInputMethodData.iconPackage = iconPackage; + mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null); + mStatusBar.setIconVisibility(mInputMethodIcon, true); + } + } + + void updateFromSettingsLocked() { + String id = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD); + if (id != null) { + try { + setInputMethodLocked(id); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Unknown input method from prefs: " + id, e); + } + } + } + + void setInputMethodLocked(String id) { + InputMethodInfo info = mMethodMap.get(id); + if (info == null) { + throw new IllegalArgumentException("Unknown id: " + mCurMethodId); + } + + if (id.equals(mCurMethodId)) { + return; + } + + final long ident = Binder.clearCallingIdentity(); + try { + mCurMethodId = id; + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, id); + + Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); + intent.putExtra("input_method_id", id); + mContext.sendBroadcast(intent); + unbindCurrentInputLocked(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + public void showSoftInput(IInputMethodClient client) { + synchronized (mMethodMap) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring showSoftInput of: " + client); + return; + } + } catch (RemoteException e) { + } + } + + showCurrentInputLocked(); + } + } + + void showCurrentInputLocked() { + mShowRequested = true; + if (!mInputShown) { + if (mCurMethod != null) { + executeOrSendMessage(mCurMethod, mCaller.obtainMessageO( + MSG_SHOW_SOFT_INPUT, mCurMethod)); + mInputShown = true; + } + } + } + + public void hideSoftInput(IInputMethodClient client) { + synchronized (mMethodMap) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring hideSoftInput of: " + client); + return; + } + } catch (RemoteException e) { + } + } + + hideCurrentInputLocked(); + } + } + + void hideCurrentInputLocked() { + if (mInputShown && mCurMethod != null) { + executeOrSendMessage(mCurMethod, mCaller.obtainMessageO( + MSG_HIDE_SOFT_INPUT, mCurMethod)); + } + mInputShown = false; + mShowRequested = false; + } + + public void windowGainedFocus(IInputMethodClient client, + boolean viewHasFocus, int softInputMode, boolean first, + int windowFlags) { + synchronized (mMethodMap) { + if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder() + + " viewHasFocus=" + viewHasFocus + + " softInputMode=#" + Integer.toHexString(softInputMode) + + " first=" + first + " flags=#" + + Integer.toHexString(windowFlags)); + + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring focus gain of: " + client); + return; + } + } catch (RemoteException e) { + } + } + + switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { + case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: + if (!viewHasFocus || (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { + if ((windowFlags&WindowManager.LayoutParams + .FLAG_ALT_FOCUSABLE_IM) == 0) { + // There is no focus view, and this window will + // be behind any soft input window, then hide the + // soft input window if it is shown. + hideCurrentInputLocked(); + } + } + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED: + // Do nothing. + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: + hideCurrentInputLocked(); + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_FIRST_VISIBLE: + if (first && !viewHasFocus && (windowFlags + & WindowManager.LayoutParams.FLAG_RESTORED_STATE) == 0) { + showCurrentInputLocked(); + } + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: + if (viewHasFocus) { + showCurrentInputLocked(); + } + break; + } + } + } + + public void showInputMethodPickerFromClient(IInputMethodClient client) { + synchronized (mMethodMap) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + Log.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client); + } + + mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER); + } + } + + public void setInputMethod(IBinder token, String id) { + synchronized (mMethodMap) { + if (mCurToken == null || mCurToken != token) { + Log.w(TAG, "Ignoring setInputMethod of token: " + token); + } + + setInputMethodLocked(id); + } + } + + public void hideMySoftInput(IBinder token) { + synchronized (mMethodMap) { + if (mCurToken == null || mCurToken != token) { + Log.w(TAG, "Ignoring hideInputMethod of token: " + token); + } + + if (mInputShown && mCurMethod != null) { + executeOrSendMessage(mCurMethod, mCaller.obtainMessageO( + MSG_HIDE_SOFT_INPUT, mCurMethod)); + } + mInputShown = false; + mShowRequested = false; + } + } + + void setEnabledSessionInMainThread(SessionState session) { + if (mEnabledSession != session) { + if (mEnabledSession != null) { + try { + if (DEBUG) Log.v(TAG, "Disabling: " + mEnabledSession); + mEnabledSession.method.setSessionEnabled( + mEnabledSession.session, false); + } catch (RemoteException e) { + } + } + mEnabledSession = session; + try { + if (DEBUG) Log.v(TAG, "Enabling: " + mEnabledSession); + session.method.setSessionEnabled( + session.session, true); + } catch (RemoteException e) { + } + } + } + + public boolean handleMessage(Message msg) { + HandlerCaller.SomeArgs args; + switch (msg.what) { + case MSG_SHOW_IM_PICKER: + showInputMethodMenu(); + return true; + + // --------------------------------------------------------- + + case MSG_UNBIND_INPUT: + try { + ((IInputMethod)msg.obj).unbindInput(); + } catch (RemoteException e) { + // There is nothing interesting about the method dying. + } + return true; + case MSG_BIND_INPUT: + args = (HandlerCaller.SomeArgs)msg.obj; + try { + ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2); + } catch (RemoteException e) { + } + return true; + case MSG_SHOW_SOFT_INPUT: + try { + ((IInputMethod)msg.obj).showSoftInput(); + } catch (RemoteException e) { + } + return true; + case MSG_HIDE_SOFT_INPUT: + try { + ((IInputMethod)msg.obj).hideSoftInput(); + } catch (RemoteException e) { + } + return true; + case MSG_ATTACH_TOKEN: + args = (HandlerCaller.SomeArgs)msg.obj; + try { + ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2); + } catch (RemoteException e) { + } + return true; + case MSG_CREATE_SESSION: + args = (HandlerCaller.SomeArgs)msg.obj; + try { + ((IInputMethod)args.arg1).createSession( + (IInputMethodCallback)args.arg2); + } catch (RemoteException e) { + } + return true; + + // --------------------------------------------------------- + + case MSG_START_INPUT: + args = (HandlerCaller.SomeArgs)msg.obj; + try { + SessionState session = (SessionState)args.arg1; + setEnabledSessionInMainThread(session); + session.method.startInput((EditorInfo)args.arg2); + } catch (RemoteException e) { + } + return true; + case MSG_RESTART_INPUT: + args = (HandlerCaller.SomeArgs)msg.obj; + try { + SessionState session = (SessionState)args.arg1; + setEnabledSessionInMainThread(session); + session.method.restartInput((EditorInfo)args.arg2); + } catch (RemoteException e) { + } + return true; + + // --------------------------------------------------------- + + case MSG_UNBIND_METHOD: + try { + ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1); + } catch (RemoteException e) { + // There is nothing interesting about the last client dying. + } + return true; + case MSG_BIND_METHOD: + args = (HandlerCaller.SomeArgs)msg.obj; + try { + ((IInputMethodClient)args.arg1).onBindMethod( + (InputBindResult)args.arg2); + } catch (RemoteException e) { + Log.w(TAG, "Client died receiving input method " + args.arg2); + } + return true; + } + return false; + } + + void buildInputMethodListLocked(ArrayList<InputMethodInfo> list, + HashMap<String, InputMethodInfo> map) { + list.clear(); + map.clear(); + + PackageManager pm = mContext.getPackageManager(); + + Object[][] buildin = {{ + DefaultInputMethod.class.getName(), + DefaultInputMethod.getMetaInfo()}}; + + List<ResolveInfo> services = pm.queryIntentServices( + new Intent(InputMethod.SERVICE_INTERFACE), + PackageManager.GET_META_DATA); + + for (int i = 0; i < services.size(); ++i) { + ResolveInfo ri = services.get(i); + ServiceInfo si = ri.serviceInfo; + ComponentName compName = new ComponentName(si.packageName, si.name); + if (!android.Manifest.permission.BIND_INPUT_METHOD.equals( + si.permission)) { + Log.w(TAG, "Skipping input method " + compName + + ": it does not require the permission " + + android.Manifest.permission.BIND_INPUT_METHOD); + continue; + } + + if (DEBUG) Log.d(TAG, "Checking " + compName); + + /* Built-in input methods are not currently supported... this will + * need to be reworked to bring them back (all input methods must + * now be published in a manifest). + */ + /* + if (compName.getPackageName().equals( + InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) { + // System build-in input methods; + String inputMethodName = null; + int kbType = 0; + String skbName = null; + + for (int j = 0; j < buildin.length; ++j) { + Object[] obj = buildin[j]; + if (compName.getClassName().equals(obj[0])) { + InputMethodMetaInfo imp = (InputMethodMetaInfo) obj[1]; + inputMethodName = imp.inputMethodName; + } + } + + InputMethodMetaInfo p = new InputMethodMetaInfo(compName, + inputMethodName, ""); + + list.add(p); + + if (DEBUG) { + Log.d(TAG, "Found a build-in input method " + p); + } + + continue; + } + */ + + try { + InputMethodInfo p = new InputMethodInfo(mContext, ri); + list.add(p); + map.put(p.getId(), p); + + if (DEBUG) { + Log.d(TAG, "Found a third-party input method " + p); + } + + } catch (XmlPullParserException e) { + Log.w(TAG, "Unable to load input method " + compName, e); + } catch (IOException e) { + Log.w(TAG, "Unable to load input method " + compName, e); + } + } + } + + // ---------------------------------------------------------------------- + + public void showInputMethodMenu() { + if (DEBUG) Log.v(TAG, "Show switching menu"); + + hideInputMethodMenu(); + + final Context context = mContext; + + final PackageManager pm = context.getPackageManager(); + + String lastInputMethodId = Settings.Secure.getString(context + .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + if (DEBUG) Log.v(TAG, "Current IME: " + lastInputMethodId); + + final List<InputMethodInfo> immis = getEnabledInputMethodList(); + + int N = (immis == null ? 0 : immis.size()); + + mItems = new CharSequence[N]; + mIms = new InputMethodInfo[N]; + + for (int i = 0; i < N; ++i) { + InputMethodInfo property = immis.get(i); + mItems[i] = property.loadLabel(pm); + mIms[i] = property; + } + + int checkedItem = 0; + for (int i = 0; i < N; ++i) { + if (mIms[i].getId().equals(lastInputMethodId)) { + checkedItem = i; + break; + } + } + + 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); + mDialogBuilder = new AlertDialog.Builder(context) + .setTitle(com.android.internal.R.string.select_input_method) + .setOnCancelListener(new OnCancelListener() { + public void onCancel(DialogInterface dialog) { + hideInputMethodMenu(); + } + }) + .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) { + InputMethodInfo im = mIms[which]; + hideInputMethodMenu(); + setInputMethodLocked(im.getId()); + } + } + }); + + synchronized (mMethodMap) { + mSwitchingDialog = mDialogBuilder.create(); + mSwitchingDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG); + mSwitchingDialog.show(); + } + } + + void hideInputMethodMenu() { + if (DEBUG) Log.v(TAG, "Hide switching menu"); + + synchronized (mMethodMap) { + if (mSwitchingDialog != null) { + mSwitchingDialog.dismiss(); + mSwitchingDialog = null; + } + + mDialogBuilder = null; + mItems = null; + mIms = null; + } + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingPermission("android.permission.DUMP") + != PackageManager.PERMISSION_GRANTED) { + + pw.println("Permission Denial: can't dump InputMethodManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + synchronized (mMethodMap) { + final Printer p = new PrintWriterPrinter(pw); + p.println("Current Input Method Manager state:"); + int N = mMethodList.size(); + p.println(" Input Methods:"); + for (int i=0; i<N; i++) { + InputMethodInfo info = mMethodList.get(i); + p.println(" InputMethod #" + i + ":"); + info.dump(p, " "); + } + p.println(" Clients:"); + for (ClientState ci : mClients.values()) { + p.println(" Client " + ci + ":"); + p.println(" client=" + ci.client); + p.println(" inputContext=" + ci.inputContext); + p.println(" sessionRequested=" + ci.sessionRequested); + p.println(" curSession=" + ci.curSession); + } + p.println(" mInputMethodIcon=" + mInputMethodIcon); + p.println(" mInputMethodData=" + mInputMethodData); + p.println(" mCurrentMethod=" + mCurMethodId); + p.println(" mCurSeq=" + mCurSeq + " mCurClient=" + mCurClient); + p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection + + " mBoundToMethod=" + mBoundToMethod); + p.println(" mCurToken=" + mCurToken); + p.println(" mCurIntent=" + mCurIntent); + p.println(" mCurMethod=" + mCurMethod); + p.println(" mEnabledSession=" + mEnabledSession); + p.println(" mShowRequested=" + mShowRequested + + " mInputShown=" + mInputShown); + p.println(" mScreenOn=" + mScreenOn); + } + } +} diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java index c67754b..77182f7 100644 --- a/services/java/com/android/server/KeyInputQueue.java +++ b/services/java/com/android/server/KeyInputQueue.java @@ -32,7 +32,7 @@ import android.view.WindowManagerPolicy; public abstract class KeyInputQueue { static final String TAG = "KeyInputQueue"; - SparseArray<InputDevice> mDevices = new SparseArray(); + SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>(); int mGlobalMetaState = 0; boolean mHaveGlobalMetaState = false; @@ -498,6 +498,7 @@ public abstract class KeyInputQueue { ev.inputDevice.mAbs.currentMove = null; } if (ev.event == ev.inputDevice.mRel.currentMove) { + if (false) Log.i(TAG, "Detach rel " + ev.event); ev.inputDevice.mRel.currentMove = null; ev.inputDevice.mRel.x = 0; ev.inputDevice.mRel.y = 0; diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 1bf60d4..831d1d2 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -57,9 +57,9 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import android.telephony.CellLocation; import android.telephony.PhoneStateListener; -import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.util.Config; import android.util.Log; @@ -145,24 +145,25 @@ public class LocationManagerService extends ILocationManager.Stub { private boolean mCellWakeLockAcquired = false; /** - * Mapping from listener IBinder to local Listener wrappers. + * Mapping from listener IBinder/PendingIntent to local Listener wrappers. */ - private final HashMap<IBinder,Listener> mListeners = - new HashMap<IBinder,Listener>(); + private final HashMap<Object,Receiver> mListeners = + new HashMap<Object,Receiver>(); /** - * Mapping from listener IBinder to a map from provider name to UpdateRecord. + * Mapping from listener IBinder/PendingIntent to a map from provider name to UpdateRecord. */ - private final HashMap<IBinder,HashMap<String,UpdateRecord>> mLocationListeners = - new HashMap<IBinder,HashMap<String,UpdateRecord>>(); + private final HashMap<Object,HashMap<String,UpdateRecord>> mLocationListeners = + new HashMap<Object,HashMap<String,UpdateRecord>>(); /** - * Mapping from listener IBinder to a map from provider name to last broadcast location. + * Mapping from listener IBinder/PendingIntent to a map from provider name to last broadcast + * location. */ - private final HashMap<IBinder,HashMap<String,Location>> mLastFixBroadcast = - new HashMap<IBinder,HashMap<String,Location>>(); - private final HashMap<IBinder,HashMap<String,Long>> mLastStatusBroadcast = - new HashMap<IBinder,HashMap<String,Long>>(); + private final HashMap<Object,HashMap<String,Location>> mLastFixBroadcast = + new HashMap<Object,HashMap<String,Location>>(); + private final HashMap<Object,HashMap<String,Long>> mLastStatusBroadcast = + new HashMap<Object,HashMap<String,Long>>(); /** * Mapping from provider name to all its UpdateRecords @@ -178,7 +179,7 @@ public class LocationManagerService extends ILocationManager.Stub { new HashMap<String,Location>(); // Proximity listeners - private ProximityListener mProximityListener = null; + private Receiver mProximityListener = null; private HashMap<PendingIntent,ProximityAlert> mProximityAlerts = new HashMap<PendingIntent,ProximityAlert>(); private HashSet<ProximityAlert> mProximitiesEntered = @@ -195,7 +196,7 @@ public class LocationManagerService extends ILocationManager.Stub { // Last known cell service state private TelephonyManager mTelephonyManager; - private ServiceState mServiceState = new ServiceState(); + private int mSignalStrength = -1; // Location collector private LocationCollector mCollector; @@ -206,11 +207,82 @@ public class LocationManagerService extends ILocationManager.Stub { // Wifi Manager private WifiManager mWifiManager; - private final class Listener implements IBinder.DeathRecipient { + /** + * A wrapper class holding either an ILocationListener or a PendingIntent to receive + * location updates. + */ + private final class Receiver implements IBinder.DeathRecipient { final ILocationListener mListener; + final PendingIntent mPendingIntent; - Listener(ILocationListener listener) { + Receiver(ILocationListener listener) { mListener = listener; + mPendingIntent = null; + } + + Receiver(PendingIntent intent) { + mPendingIntent = intent; + mListener = null; + } + + public Object getKey() { + if (mListener != null) { + return mListener.asBinder(); + } else { + return mPendingIntent; + } + } + + public boolean isListener() { + return mListener != null; + } + + public boolean isPendingIntent() { + return mPendingIntent != null; + } + + public ILocationListener getListener() { + if (mListener != null) { + return mListener; + } + throw new IllegalStateException("Request for non-existent listener"); + } + + public PendingIntent getPendingIntent() { + if (mPendingIntent != null) { + return mPendingIntent; + } + throw new IllegalStateException("Request for non-existent intent"); + } + + public void onStatusChanged(String provider, int status, Bundle extras) + throws RemoteException { + if (mListener != null) { + mListener.onStatusChanged(provider, status, extras); + } else { + Intent statusChanged = new Intent(); + statusChanged.putExtras(extras); + statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status); + try { + mPendingIntent.send(mContext, 0, statusChanged, null, null); + } catch (PendingIntent.CanceledException e) { + _removeUpdates(this); + } + } + } + + public void onLocationChanged(Location location) throws RemoteException { + if (mListener != null) { + mListener.onLocationChanged(location); + } else { + Intent locationChanged = new Intent(); + locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location); + try { + mPendingIntent.send(mContext, 0, locationChanged, null, null); + } catch (PendingIntent.CanceledException e) { + _removeUpdates(this); + } + } } public void binderDied() { @@ -218,7 +290,7 @@ public class LocationManagerService extends ILocationManager.Stub { Log.d(TAG, "Location listener died"); } synchronized (mLocationListeners) { - _removeUpdates(mListener); + _removeUpdates(this); } } } @@ -228,7 +300,7 @@ public class LocationManagerService extends ILocationManager.Stub { String s = null; try { File f = new File(LocationManager.SYSTEM_DIR + "/location." - + provider); + + provider); if (!f.exists()) { return null; } @@ -443,13 +515,22 @@ public class LocationManagerService extends ILocationManager.Stub { // Create location collector mCollector = new LocationCollector(mMasfClient); + // Alarm manager, needs to be done before calling loadProviders() below + mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + + // Create a wake lock, needs to be done before calling loadProviders() below + PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); + // Load providers loadProviders(); // Listen for Radio changes mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); mTelephonyManager.listen(mPhoneStateListener, - PhoneStateListener.LISTEN_SERVICE_STATE | PhoneStateListener.LISTEN_CELL_LOCATION); + PhoneStateListener.LISTEN_CELL_LOCATION | + PhoneStateListener.LISTEN_SIGNAL_STRENGTH | + PhoneStateListener.LISTEN_DATA_CONNECTION_STATE); // Register for Network (Wifi or Mobile) updates NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver(); @@ -460,9 +541,6 @@ public class LocationManagerService extends ILocationManager.Stub { networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION); context.registerReceiver(networkReceiver, networkIntentFilter); - // Alarm manager - mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - // Register for power updates PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); @@ -472,10 +550,6 @@ public class LocationManagerService extends ILocationManager.Stub { intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); context.registerReceiver(powerStateReceiver, intentFilter); - // Create a wake lock - PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); - // Get the wifi manager mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); @@ -510,8 +584,8 @@ public class LocationManagerService extends ILocationManager.Stub { } // Use system settings ContentResolver resolver = mContext.getContentResolver(); - String allowedProviders = Settings.System.getString(resolver, - Settings.System.LOCATION_PROVIDERS_ALLOWED); + String allowedProviders = Settings.Secure.getString(resolver, + Settings.Secure.LOCATION_PROVIDERS_ALLOWED); return ((allowedProviders != null) && (allowedProviders.contains(provider))); } @@ -638,16 +712,28 @@ public class LocationManagerService extends ILocationManager.Stub { } synchronized (mRecordsByProvider) { - HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); if (records != null) { for (UpdateRecord record : records) { - // Sends a notification message to the listener + // Sends a notification message to the receiver try { - if (enabled) { - record.mListener.mListener.onProviderEnabled(provider); + Receiver receiver = record.mReceiver; + if (receiver.isListener()) { + if (enabled) { + receiver.getListener().onProviderEnabled(provider); + } else { + receiver.getListener().onProviderDisabled(provider); + } } else { - record.mListener.mListener.onProviderDisabled(provider); + PendingIntent intent = receiver.getPendingIntent(); + Intent providerIntent = new Intent(); + providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled); + try { + receiver.getPendingIntent().send(mContext, 0, + providerIntent, null, null); + } catch (PendingIntent.CanceledException e) { + _removeUpdates(receiver); + } } } catch (RemoteException e) { // The death link will clean this up. @@ -694,15 +780,15 @@ public class LocationManagerService extends ILocationManager.Stub { private class UpdateRecord { String mProvider; - Listener mListener; + Receiver mReceiver; long mMinTime; float mMinDistance; String[] mPackages; - UpdateRecord(String provider, long minTime, float minDistance, Listener listener, - String[] packages) { + UpdateRecord(String provider, long minTime, float minDistance, + Receiver receiver, String[] packages) { mProvider = provider; - mListener = listener; + mReceiver = receiver; mMinTime = minTime; mMinDistance = minDistance; mPackages = packages; @@ -740,7 +826,20 @@ public class LocationManagerService extends ILocationManager.Stub { long minTime, float minDistance, ILocationListener listener) { try { - _requestLocationUpdates(provider, minTime, minDistance, listener); + _requestLocationUpdates(provider, minTime, minDistance, + new Receiver(listener)); + } catch (SecurityException se) { + throw se; + } catch (Exception e) { + Log.e(TAG, "requestUpdates got exception:", e); + } + } + + public void requestLocationUpdatesPI(String provider, + long minTime, float minDistance, PendingIntent intent) { + try { + _requestLocationUpdates(provider, minTime, minDistance, + new Receiver(intent)); } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -749,9 +848,10 @@ public class LocationManagerService extends ILocationManager.Stub { } private void _requestLocationUpdates(String provider, - long minTime, float minDistance, ILocationListener listener) { + long minTime, float minDistance, Receiver receiver) { + Object key = receiver.getKey(); if (Config.LOGD) { - Log.d(TAG, "_requestLocationUpdates: listener = " + listener.asBinder()); + Log.d(TAG, "_requestLocationUpdates: listener = " + key); } LocationProviderImpl impl = LocationProviderImpl.getProvider(provider); @@ -766,24 +866,23 @@ public class LocationManagerService extends ILocationManager.Stub { // so wakelock calls will succeed long identity = Binder.clearCallingIdentity(); try { - Listener myListener = new Listener(listener); - UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, myListener, packages); - + UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, packages); synchronized (mLocationListeners) { - IBinder binder = listener.asBinder(); - if (mListeners.get(binder) == null) { + if (mListeners.get(key) == null) { try { - binder.linkToDeath(myListener, 0); - mListeners.put(binder, myListener); + if (receiver.isListener()) { + receiver.getListener().asBinder().linkToDeath(receiver, 0); + } + mListeners.put(key, receiver); } catch (RemoteException e) { return; } } - HashMap<String,UpdateRecord> records = mLocationListeners.get(binder); + HashMap<String,UpdateRecord> records = mLocationListeners.get(key); if (records == null) { records = new HashMap<String,UpdateRecord>(); - mLocationListeners.put(binder, records); + mLocationListeners.put(key, records); } UpdateRecord oldRecord = records.put(provider, r); if (oldRecord != null) { @@ -809,9 +908,12 @@ public class LocationManagerService extends ILocationManager.Stub { } else { try { // Notify the listener that updates are currently disabled - listener.onProviderDisabled(provider); + if (receiver.isListener()) { + receiver.getListener().onProviderDisabled(provider); + } } catch(RemoteException e) { - Log.w(TAG, "RemoteException calling onProviderDisabled on " + listener); + Log.w(TAG, "RemoteException calling onProviderDisabled on " + + receiver.getListener()); } } } @@ -822,7 +924,7 @@ public class LocationManagerService extends ILocationManager.Stub { public void removeUpdates(ILocationListener listener) { try { - _removeUpdates(listener); + _removeUpdates(new Receiver(listener)); } catch (SecurityException se) { throw se; } catch (Exception e) { @@ -830,24 +932,34 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void _removeUpdates(ILocationListener listener) { + public void removeUpdatesPI(PendingIntent intent) { + try { + _removeUpdates(new Receiver(intent)); + } catch (SecurityException se) { + throw se; + } catch (Exception e) { + Log.e(TAG, "removeUpdates got exception:", e); + } + } + + private void _removeUpdates(Receiver receiver) { + Object key = receiver.getKey(); if (Config.LOGD) { - Log.d(TAG, "_removeUpdates: listener = " + listener.asBinder()); + Log.d(TAG, "_removeUpdates: listener = " + key); } // so wakelock calls will succeed long identity = Binder.clearCallingIdentity(); try { synchronized (mLocationListeners) { - IBinder binder = listener.asBinder(); - Listener myListener = mListeners.remove(binder); - if (myListener != null) { - binder.unlinkToDeath(myListener, 0); + Receiver myReceiver = mListeners.remove(key); + if ((myReceiver != null) && (myReceiver.isListener())) { + myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0); } // Record which providers were associated with this listener HashSet<String> providers = new HashSet<String>(); - HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(binder); + HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(key); if (oldRecords != null) { // Call dispose() on the obsolete update records. for (UpdateRecord record : oldRecords.values()) { @@ -862,9 +974,9 @@ public class LocationManagerService extends ILocationManager.Stub { providers.addAll(oldRecords.keySet()); } - mLocationListeners.remove(binder); - mLastFixBroadcast.remove(binder); - mLastStatusBroadcast.remove(binder); + mLocationListeners.remove(key); + mLastFixBroadcast.remove(key); + mLastStatusBroadcast.remove(key); // See if the providers associated with this listener have any // other listeners; if one does, inform it of the new smallest minTime @@ -894,7 +1006,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - updateWakelockStatus(mScreenOn); + updateWakelockStatus(mScreenOn); } } finally { Binder.restoreCallingIdentity(identity); @@ -905,6 +1017,11 @@ public class LocationManagerService extends ILocationManager.Stub { if (mGpsLocationProvider == null) { return false; } + if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) != + PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); + } + try { mGpsLocationProvider.addGpsStatusListener(listener); } catch (RemoteException e) { @@ -996,7 +1113,6 @@ public class LocationManagerService extends ILocationManager.Stub { ArrayList<PendingIntent> intentsToRemove = null; for (ProximityAlert alert : mProximityAlerts.values()) { - PendingIntent intent = alert.getIntent(); long expiration = alert.getExpiration(); @@ -1115,7 +1231,7 @@ public class LocationManagerService extends ILocationManager.Stub { mProximityAlerts.put(intent, alert); if (mProximityListener == null) { - mProximityListener = new ProximityListener(); + mProximityListener = new Receiver(new ProximityListener()); LocationProvider provider = LocationProviderImpl.getProvider( LocationManager.GPS_PROVIDER); @@ -1345,15 +1461,15 @@ public class LocationManagerService extends ILocationManager.Stub { // Broadcast location or status to all listeners for (UpdateRecord r : records) { - ILocationListener listener = r.mListener.mListener; - IBinder binder = listener.asBinder(); + Receiver receiver = r.mReceiver; + Object key = receiver.getKey(); // Broadcast location only if it is valid if (locationValid) { - HashMap<String,Location> map = mLastFixBroadcast.get(binder); + HashMap<String,Location> map = mLastFixBroadcast.get(key); if (map == null) { map = new HashMap<String,Location>(); - mLastFixBroadcast.put(binder, map); + mLastFixBroadcast.put(key, map); } Location lastLoc = map.get(provider); if ((lastLoc == null) || shouldBroadcast(loc, lastLoc, r)) { @@ -1364,19 +1480,19 @@ public class LocationManagerService extends ILocationManager.Stub { lastLoc.set(loc); } try { - listener.onLocationChanged(loc); + receiver.onLocationChanged(loc); } catch (RemoteException doe) { - Log.w(TAG, "RemoteException calling onLocationChanged on " + listener); - _removeUpdates(listener); + Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver); + _removeUpdates(receiver); } } } // Broadcast status message - HashMap<String,Long> statusMap = mLastStatusBroadcast.get(binder); + HashMap<String,Long> statusMap = mLastStatusBroadcast.get(key); if (statusMap == null) { statusMap = new HashMap<String,Long>(); - mLastStatusBroadcast.put(binder, statusMap); + mLastStatusBroadcast.put(key, statusMap); } long prevStatusUpdateTime = (statusMap.get(provider) != null) ? statusMap.get(provider) : 0; @@ -1386,10 +1502,10 @@ public class LocationManagerService extends ILocationManager.Stub { statusMap.put(provider, newStatusUpdateTime); try { - listener.onStatusChanged(provider, status, extras); + receiver.onStatusChanged(provider, status, extras); } catch (RemoteException doe) { - Log.w(TAG, "RemoteException calling onStatusChanged on " + listener); - _removeUpdates(listener); + Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver); + _removeUpdates(receiver); } } } @@ -1423,7 +1539,8 @@ public class LocationManagerService extends ILocationManager.Stub { } if ((mWakeLockAcquireTime != 0) && - (SystemClock.elapsedRealtime() - mWakeLockAcquireTime > MAX_TIME_FOR_WAKE_LOCK)) { + (SystemClock.elapsedRealtime() - mWakeLockAcquireTime + > MAX_TIME_FOR_WAKE_LOCK)) { removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); removeMessages(MESSAGE_RELEASE_WAKE_LOCK); @@ -1449,7 +1566,7 @@ public class LocationManagerService extends ILocationManager.Stub { acquireWakeLock(); } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) { log("LocationWorkerHandler: Release"); - + // Update wakelock status so the next alarm is set before releasing wakelock updateWakelockStatus(mScreenOn); releaseWakeLock(); @@ -1462,22 +1579,24 @@ public class LocationManagerService extends ILocationManager.Stub { } PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + + private CellState mLastCellState = null; @Override public void onCellLocationChanged(CellLocation cellLocation) { try { - ServiceState serviceState = mServiceState; + int asu = mSignalStrength; // Gets cell state - CellState cellState = new CellState(serviceState, cellLocation); + mLastCellState = new CellState(mTelephonyManager, cellLocation, asu); // Notify collector - mCollector.updateCellState(cellState); + mCollector.updateCellState(mLastCellState); // Updates providers List<LocationProviderImpl> providers = LocationProviderImpl.getProviders(); for (LocationProviderImpl provider : providers) { if (provider.requiresCell()) { - provider.updateCellState(cellState); + provider.updateCellState(mLastCellState); } } } catch (Exception e) { @@ -1486,8 +1605,19 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void onServiceStateChanged(ServiceState serviceState) { - mServiceState = serviceState; + public void onSignalStrengthChanged(int asu) { + mSignalStrength = asu; + + if (mLastCellState != null) { + mLastCellState.updateSignalStrength(asu); + } + } + + @Override + public void onDataConnectionStateChanged(int state) { + if (mLastCellState != null) { + mLastCellState.updateRadioType(mTelephonyManager); + } } }; @@ -1578,7 +1708,8 @@ public class LocationManagerService extends ILocationManager.Stub { } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) { - final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false); + final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, + false); if (!enabled) { // When GPS is disabled, we are OK to release wake-lock @@ -1608,7 +1739,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (screenOn) { startGps(); } else if (mScreenOn && !screenOn) { - + // We just turned the screen off so stop navigating stopGps(); } @@ -1641,13 +1772,23 @@ public class LocationManagerService extends ILocationManager.Stub { } private void acquireWakeLock() { + try { + acquireWakeLockX(); + } catch (Exception e) { + // This is to catch a runtime exception thrown when we try to release an + // already released lock. + Log.e(TAG, "exception in acquireWakeLock()", e); + } + } + + private void acquireWakeLockX() { if (mWakeLock.isHeld()) { log("Must release wakelock before acquiring"); mWakeLockAcquireTime = 0; mWakeLock.release(); } - boolean networkActive = (mNetworkLocationProvider != null) + boolean networkActive = (mNetworkLocationProvider != null) && mNetworkLocationProvider.isLocationTracking(); boolean gpsActive = (mGpsLocationProvider != null) && mGpsLocationProvider.isLocationTracking(); @@ -1668,7 +1809,7 @@ public class LocationManagerService extends ILocationManager.Stub { // Start the gps provider startGps(); - + // Acquire cell lock if (mCellWakeLockAcquired) { // Lock is already acquired @@ -1700,7 +1841,7 @@ public class LocationManagerService extends ILocationManager.Stub { } private void startGps() { - boolean gpsActive = (mGpsLocationProvider != null) + boolean gpsActive = (mGpsLocationProvider != null) && mGpsLocationProvider.isLocationTracking(); if (gpsActive) { mGpsLocationProvider.startNavigating(); @@ -1708,7 +1849,7 @@ public class LocationManagerService extends ILocationManager.Stub { } private void stopGps() { - boolean gpsActive = mGpsLocationProvider != null + boolean gpsActive = mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking(); if (gpsActive) { mGpsLocationProvider.stopNavigating(); @@ -1716,6 +1857,16 @@ public class LocationManagerService extends ILocationManager.Stub { } private void releaseWakeLock() { + try { + releaseWakeLockX(); + } catch (Exception e) { + // This is to catch a runtime exception thrown when we try to release an + // already released lock. + Log.e(TAG, "exception in releaseWakeLock()", e); + } + } + + private void releaseWakeLockX() { // Release wifi lock WifiManager.WifiLock wifiLock = getWifiWakelock(); if (wifiLock != null) { @@ -1726,22 +1877,22 @@ public class LocationManagerService extends ILocationManager.Stub { } if (!mScreenOn) { - + // Stop the gps stopGps(); } - + // Release cell lock if (mCellWakeLockAcquired) { mTelephonyManager.disableLocationUpdates(); mCellWakeLockAcquired = false; } - + // Notify NetworkLocationProvider if (mNetworkLocationProvider != null) { mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired); } - + // Release wake lock mWakeLockAcquireTime = 0; if (mWakeLock.isHeld()) { @@ -1751,7 +1902,7 @@ public class LocationManagerService extends ILocationManager.Stub { log("Can't release wakelock again!"); } } - + // Geocoder public String getFromLocation(double latitude, double longitude, int maxResults, @@ -1903,14 +2054,29 @@ public class LocationManagerService extends ILocationManager.Stub { return mSupportsSpeed; } } + + private void checkMockPermissions() { + boolean allowMocks = false; + try { + allowMocks = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ALLOW_MOCK_LOCATION) == 1; + } catch (SettingNotFoundException e) { + // Do nothing + } + if (!allowMocks) { + throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting"); + } - public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite, - boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, - boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); - } + } + } + + public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite, + boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, + boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { + checkMockPermissions(); MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, @@ -1923,10 +2089,7 @@ public class LocationManagerService extends ILocationManager.Stub { } public void removeTestProvider(String provider) { - if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); - } + checkMockPermissions(); LocationProviderImpl p = LocationProviderImpl.getProvider(provider); if (p == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); @@ -1936,10 +2099,7 @@ public class LocationManagerService extends ILocationManager.Stub { } public void setTestProviderLocation(String provider, Location loc) { - if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); - } + checkMockPermissions(); if (LocationProviderImpl.getProvider(provider) == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } @@ -1947,10 +2107,7 @@ public class LocationManagerService extends ILocationManager.Stub { } public void clearTestProviderLocation(String provider) { - if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); - } + checkMockPermissions(); if (LocationProviderImpl.getProvider(provider) == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } @@ -1958,10 +2115,7 @@ public class LocationManagerService extends ILocationManager.Stub { } public void setTestProviderEnabled(String provider, boolean enabled) { - if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); - } + checkMockPermissions(); if (LocationProviderImpl.getProvider(provider) == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } @@ -1976,10 +2130,7 @@ public class LocationManagerService extends ILocationManager.Stub { } public void clearTestProviderEnabled(String provider) { - if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); - } + checkMockPermissions(); if (LocationProviderImpl.getProvider(provider) == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } @@ -1989,10 +2140,7 @@ public class LocationManagerService extends ILocationManager.Stub { } public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) { - if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); - } + checkMockPermissions(); if (LocationProviderImpl.getProvider(provider) == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } @@ -2002,10 +2150,7 @@ public class LocationManagerService extends ILocationManager.Stub { } public void clearTestProviderStatus(String provider) { - if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); - } + checkMockPermissions(); if (LocationProviderImpl.getProvider(provider) == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 6f29332..03480d1 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -22,6 +22,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.net.Uri; import android.os.IMountService; import android.os.Environment; @@ -59,10 +60,11 @@ class MountService extends IMountService.Stub { private Notification mUsbStorageNotification; private class SdDoorListener extends UEventObserver { - static final String SD_DOOR_UEVENT_MATCH = "DEVPATH=/class/switch/sd-door"; + static final String SD_DOOR_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/sd-door"; + static final String SD_DOOR_SWITCH_NAME = "sd-door"; public void onUEvent(UEvent event) { - if ("sd-door".equals(event.get("SWITCH_NAME"))) { + if (SD_DOOR_SWITCH_NAME.equals(event.get("SWITCH_NAME"))) { sdDoorStateChanged(event.get("SWITCH_STATE")); } } @@ -127,9 +129,7 @@ class MountService extends IMountService.Stub { throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission"); } - // notify listeners that we are trying to eject - notifyMediaEject(mountPath); - // tell usbd to unmount the media + // tell mountd to unmount the media mListener.ejectMedia(mountPath); } @@ -269,22 +269,24 @@ class MountService extends IMountService.Stub { * @return A {@link Notification} that leads to the dialog to enable USB storage. */ private synchronized Notification getUsbStorageNotification() { + Resources r = Resources.getSystem(); + CharSequence title = + r.getText(com.android.internal.R.string.usb_storage_notification_title); + CharSequence message = + r.getText(com.android.internal.R.string.usb_storage_notification_message); + if (mUsbStorageNotification == null) { - CharSequence title = - mContext.getString(com.android.internal.R.string.usb_storage_notification_title); - CharSequence message = - mContext.getString(com.android.internal.R.string.usb_storage_notification_message); - mUsbStorageNotification = new Notification(); mUsbStorageNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb; - mUsbStorageNotification.tickerText = title; mUsbStorageNotification.when = 0; mUsbStorageNotification.flags = Notification.FLAG_AUTO_CANCEL; mUsbStorageNotification.defaults |= Notification.DEFAULT_SOUND; - mUsbStorageNotification.setLatestEventInfo(mContext, title, message, - getUsbStorageDialogIntent()); } + mUsbStorageNotification.tickerText = title; + mUsbStorageNotification.setLatestEventInfo(mContext, title, message, + getUsbStorageDialogIntent()); + return mUsbStorageNotification; } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 14ac1be..eb9ebe9 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -72,7 +72,7 @@ class NotificationManagerService extends INotificationManager.Stub private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; - private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_RING; + private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; final Context mContext; final IActivityManager mAm; diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index c000d8a..e86ff02 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -55,6 +55,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; +import android.content.pm.PackageParser.Package; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -177,7 +178,7 @@ class PackageManagerService extends IPackageManager.Stub { // Lock for state used when installing and doing other long running // operations. Methods that must be called with this lock held have // the prefix "LI". - Object mInstallLock = new Object(); + final Object mInstallLock = new Object(); // These are the directories in the 3rd party applications installed dir // that we have currently loaded packages from. Keys are the application's @@ -674,7 +675,7 @@ class PackageManagerService extends IPackageManager.Stub { } } - int[] appendInt(int[] cur, int val) { + static int[] appendInt(int[] cur, int val) { if (cur == null) { return new int[] { val }; } @@ -690,7 +691,7 @@ class PackageManagerService extends IPackageManager.Stub { return ret; } - int[] appendInts(int[] cur, int[] add) { + static int[] appendInts(int[] cur, int[] add) { if (add == null) return cur; if (cur == null) return add; final int N = add.length; @@ -718,6 +719,9 @@ class PackageManagerService extends IPackageManager.Stub { if (p != null) { return generatePackageInfo(p, flags); } + if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + return generatePackageInfoFromSettingsLP(packageName, flags); + } } return null; } @@ -725,6 +729,14 @@ class PackageManagerService extends IPackageManager.Stub { public int getPackageUid(String packageName) { synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); + if(p != null) { + return p.applicationInfo.uid; + } + PackageSetting ps = mSettings.mPackages.get(packageName); + if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) { + return -1; + } + p = ps.pkg; return p != null ? p.applicationInfo.uid : -1; } } @@ -796,12 +808,39 @@ class PackageManagerService extends IPackageManager.Stub { } } + private ApplicationInfo generateApplicationInfoFromSettingsLP(String packageName, int flags) { + PackageSetting ps = mSettings.mPackages.get(packageName); + if(ps != null) { + if(ps.pkg == null) { + PackageInfo pInfo = generatePackageInfoFromSettingsLP(packageName, flags); + if(pInfo != null) { + return pInfo.applicationInfo; + } + return null; + } + return PackageParser.generateApplicationInfo(ps.pkg, flags); + } + return null; + } + + private PackageInfo generatePackageInfoFromSettingsLP(String packageName, int flags) { + PackageSetting ps = mSettings.mPackages.get(packageName); + if(ps != null) { + if(ps.pkg == null) { + ps.pkg = new PackageParser.Package(packageName); + ps.pkg.applicationInfo.packageName = packageName; + } + return generatePackageInfo(ps.pkg, flags); + } + return null; + } + public ApplicationInfo getApplicationInfo(String packageName, int flags) { synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); if (Config.LOGV) Log.v( - TAG, "getApplicationInfo " + packageName - + ": " + p); + TAG, "getApplicationInfo " + packageName + + ": " + p); if (p != null) { // Note: isEnabledLP() does not apply here - always return info return PackageParser.generateApplicationInfo(p, flags); @@ -809,10 +848,14 @@ class PackageManagerService extends IPackageManager.Stub { if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; } + if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + return generateApplicationInfoFromSettingsLP(packageName, flags); + } } return null; } + public void freeApplicationCache(final long freeStorageSize, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); @@ -1063,6 +1106,19 @@ class PackageManagerService extends IPackageManager.Stub { } return null; } + + public int getUidForSharedUser(String sharedUserName) { + if(sharedUserName == null) { + return -1; + } + synchronized (mPackages) { + SharedUserSetting suid = mSettings.getSharedUserLP(sharedUserName, 0, false); + if(suid == null) { + return -1; + } + return suid.userId; + } + } public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags) { @@ -1391,37 +1447,63 @@ class PackageManagerService extends IPackageManager.Stub { queryIntent(null, intent, resolvedType, flags); } } - + public List<PackageInfo> getInstalledPackages(int flags) { ArrayList<PackageInfo> finalList = new ArrayList<PackageInfo>(); synchronized (mPackages) { - Iterator<PackageParser.Package> i = mPackages.values().iterator(); - while (i.hasNext()) { - final PackageParser.Package p = i.next(); - PackageInfo pi = generatePackageInfo(p, flags); - if (pi != null) { - finalList.add(pi); + if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + Iterator<PackageSetting> i = mSettings.mPackages.values().iterator(); + while (i.hasNext()) { + final PackageSetting ps = i.next(); + PackageInfo psPkg = generatePackageInfoFromSettingsLP(ps.name, flags); + if(psPkg != null) { + finalList.add(psPkg); + } + } + } + else { + Iterator<PackageParser.Package> i = mPackages.values().iterator(); + while (i.hasNext()) { + final PackageParser.Package p = i.next(); + if (p.applicationInfo != null) { + PackageInfo pi = generatePackageInfo(p, flags); + if(pi != null) { + finalList.add(pi); + } + } } } } - return finalList; } public List<ApplicationInfo> getInstalledApplications(int flags) { ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>(); - - synchronized (mPackages) { - Iterator<PackageParser.Package> i = mPackages.values().iterator(); - while (i.hasNext()) { - final PackageParser.Package p = i.next(); - if (p.applicationInfo != null) { - finalList.add(p.applicationInfo); + synchronized(mPackages) { + if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + Iterator<PackageSetting> i = mSettings.mPackages.values().iterator(); + while (i.hasNext()) { + final PackageSetting ps = i.next(); + ApplicationInfo ai = generateApplicationInfoFromSettingsLP(ps.name, flags); + if(ai != null) { + finalList.add(ai); + } + } + } + else { + Iterator<PackageParser.Package> i = mPackages.values().iterator(); + while (i.hasNext()) { + final PackageParser.Package p = i.next(); + if (p.applicationInfo != null) { + ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags); + if(ai != null) { + finalList.add(ai); + } + } } } } - return finalList; } @@ -1569,7 +1651,7 @@ class PackageManagerService extends IPackageManager.Stub { PackageParser.Package pkg, File srcFile, int parseFlags) { if (GET_CERTIFICATES) { if (ps == null || !ps.codePath.equals(srcFile) - || ps.timeStamp != srcFile.lastModified()) { + || ps.getTimeStamp() != srcFile.lastModified()) { Log.i(TAG, srcFile.toString() + " changed; collecting certs"); if (!pp.collectCertificates(pkg, parseFlags)) { mLastScanError = pp.getParseError(); @@ -1580,6 +1662,10 @@ class PackageManagerService extends IPackageManager.Stub { return true; } + /* + * Scan a package and return the newly parsed package. + * Returns null in case of errors and the error code is stored in mLastScanError + */ private PackageParser.Package scanPackageLI(File scanFile, File destCodeFile, File destResourceFile, int parseFlags, int scanMode) { @@ -1595,11 +1681,29 @@ class PackageManagerService extends IPackageManager.Stub { return null; } PackageSetting ps; + PackageSetting updatedPkg; synchronized (mPackages) { ps = mSettings.peekPackageLP(pkg.packageName, scanFile.toString()); + updatedPkg = mSettings.mDisabledSysPackages.get(pkg.packageName); + } + if (updatedPkg != null) { + // An updated system app will not have the PARSE_IS_SYSTEM flag set initially + parseFlags |= PackageParser.PARSE_IS_SYSTEM; + } + if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { + // Check for updated system applications here + if ((updatedPkg != null) && (ps == null)) { + // The system package has been updated and the code path does not match + // Ignore entry. Just return + Log.w(TAG, "Package:" + pkg.packageName + + " has been updated. Ignoring the one from path:"+scanFile); + mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; + return null; + } } if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) { + Log.i(TAG, "Failed verifying certificates for package:" + pkg.packageName); return null; } // The apk is forward locked (not public) if its code and resources @@ -1607,6 +1711,7 @@ class PackageManagerService extends IPackageManager.Stub { if (ps != null && !ps.codePath.equals(ps.resourcePath)) { scanMode |= SCAN_FORWARD_LOCKED; } + // Note that we invoke the following method only if we are about to unpack an application return scanPackageLI(scanFile, destCodeFile, destResourceFile, pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE); } @@ -1652,6 +1757,7 @@ class PackageManagerService extends IPackageManager.Stub { mScanningPath = scanFile; if (pkg == null) { + mLastScanError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; return null; } @@ -1681,7 +1787,7 @@ class PackageManagerService extends IPackageManager.Stub { mResolveActivity.packageName = mAndroidApplication.packageName; mResolveActivity.processName = mAndroidApplication.processName; mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; - mResolveActivity.flags = 0; + mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; mResolveActivity.theme = com.android.internal.R.style.Theme_Dialog_Alert; mResolveActivity.exported = true; mResolveActivity.enabled = true; @@ -1748,6 +1854,11 @@ class PackageManagerService extends IPackageManager.Stub { mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } + synchronized(mPackages) { + if(mSettings.mDisabledSysPackages.get(pkg.packageName) != null) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + } + } pkg.applicationInfo.uid = pkgSetting.userId; pkg.mExtras = pkgSetting; @@ -1755,6 +1866,7 @@ class PackageManagerService extends IPackageManager.Stub { if (!verifySignaturesLP(pkgSetting, pkg, parseFlags, (scanMode&SCAN_UPDATE_SIGNATURE) != 0)) { if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) == 0) { + mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; return null; } // The signature has changed, but this package is in the system @@ -1768,6 +1880,7 @@ class PackageManagerService extends IPackageManager.Stub { if (pkgSetting.sharedUser != null) { if (!pkgSetting.sharedUser.signatures.mergeSignatures( pkg.mSignatures, false)) { + mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; return null; } } @@ -1782,6 +1895,7 @@ class PackageManagerService extends IPackageManager.Stub { String msg = "System package " + pkg.packageName + " could not have data directory erased after signature change."; reportSettingsProblem(Log.WARN, msg); + mLastScanError = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; return null; } } @@ -1792,7 +1906,7 @@ class PackageManagerService extends IPackageManager.Stub { long scanFileTime = scanFile.lastModified(); final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0; - final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.timeStamp; + final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.getTimeStamp(); // At this point we know it is okay to accept the package, though // errors can still happen as we try to install... @@ -3037,48 +3151,363 @@ class PackageManagerService extends IPackageManager.Stub { PackageRemovedInfo removedInfo; } - PackageInstalledInfo installPackageLI(Uri packageURI, int flags) { - int returnCode = PackageManager.INSTALL_SUCCEEDED; - String installedPackageName = null; - int installedPackageUid = -1; - PackageParser.Package installedPackage = null; + /* + * Install a non-existing package. + */ + private void installNewPackageLI(String pkgName, int parseFlags, + File tmpPackageFile, + String destFilePath, File destPackageFile, File destResourceFile, + PackageParser.Package pkg, boolean forwardLocked, + PackageInstalledInfo res) { + // Remember this for later, in case we need to rollback this install + boolean dataDirExists = (new File(mAppDataDir, pkgName)).exists(); + res.name = pkgName; + synchronized(mPackages) { + if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(destFilePath)) { + // Don't allow installation over an existing package with the same name. + Log.w(TAG, "Attempt to re-install " + pkgName + + " without first uninstalling."); + res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; + return; + } + } + if (destPackageFile.exists()) { + // It's safe to do this because we know (from the above check) that the file + // isn't currently used for an installed package. + destPackageFile.delete(); + } + mLastScanError = PackageManager.INSTALL_SUCCEEDED; + PackageParser.Package newPackage = scanPackageLI(tmpPackageFile, destPackageFile, + destResourceFile, pkg, parseFlags, + SCAN_MONITOR | SCAN_FORCE_DEX + | SCAN_UPDATE_SIGNATURE + | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)); + if (newPackage == null) { + Log.w(TAG, "Package couldn't be installed in " + destPackageFile); + if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; + } + } else { + updateSettingsLI(pkgName, tmpPackageFile, + destFilePath, destPackageFile, + destResourceFile, pkg, + newPackage, + true, + forwardLocked, + res); + // delete the partially installed application. the data directory will have to be + // restored if it was already existing + if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { + // remove package from internal structures. Note that we want deletePackageX to + // delete the package data and cache directories that it created in + // scanPackageLocked, unless those directories existed before we even tried to + // install. + deletePackageLI( + pkgName, true, + dataDirExists ? PackageManager.DONT_DELETE_DATA : 0, + res.removedInfo); + } + } + } + + private void replacePackageLI(String pkgName, int parseFlags, + File tmpPackageFile, + String destFilePath, File destPackageFile, File destResourceFile, + PackageParser.Package pkg, boolean forwardLocked, + PackageInstalledInfo res) { + PackageParser.Package deletedPackage; + // First find the old package info and check signatures + synchronized(mPackages) { + deletedPackage = mPackages.get(pkgName); + if(checkSignaturesLP(pkg, deletedPackage) != PackageManager.SIGNATURE_MATCH) { + res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + return; + } + } + boolean sysPkg = ((deletedPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); + if(sysPkg) { + replaceSystemPackageLI(deletedPackage, + parseFlags, + tmpPackageFile, destFilePath, + destPackageFile, destResourceFile, pkg, forwardLocked, res); + } else { + replaceNonSystemPackageLI(deletedPackage, parseFlags, tmpPackageFile, destFilePath, + destPackageFile, destResourceFile, pkg, forwardLocked, res); + } + } + + private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, int parseFlags, + File tmpPackageFile, + String destFilePath, File destPackageFile, File destResourceFile, + PackageParser.Package pkg, boolean forwardLocked, + PackageInstalledInfo res) { + PackageParser.Package newPackage = null; + String pkgName = deletedPackage.packageName; + boolean deletedPkg = true; + boolean updatedSettings = false; + // First delete the existing package while retaining the data directory + if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA, + res.removedInfo)) { + // If the existing package was'nt successfully deleted + res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; + deletedPkg = false; + } else { + // Successfully deleted the old package. Now proceed with re-installation + mLastScanError = PackageManager.INSTALL_SUCCEEDED; + newPackage = scanPackageLI(tmpPackageFile, destPackageFile, + destResourceFile, pkg, parseFlags, + SCAN_MONITOR | SCAN_FORCE_DEX + | SCAN_UPDATE_SIGNATURE + | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)); + if (newPackage == null) { + Log.w(TAG, "Package couldn't be installed in " + destPackageFile); + if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; + } + } else { + updateSettingsLI(pkgName, tmpPackageFile, + destFilePath, destPackageFile, + destResourceFile, pkg, + newPackage, + true, + forwardLocked, + res); + updatedSettings = true; + } + } + + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + // If we deleted an exisiting package, the old source and resource files that we + // were keeping around in case we needed them (see below) can now be deleted + final ApplicationInfo deletedPackageAppInfo = deletedPackage.applicationInfo; + final ApplicationInfo installedPackageAppInfo = + newPackage.applicationInfo; + if (!deletedPackageAppInfo.sourceDir + .equals(installedPackageAppInfo.sourceDir)) { + new File(deletedPackageAppInfo.sourceDir).delete(); + } + if (!deletedPackageAppInfo.publicSourceDir + .equals(installedPackageAppInfo.publicSourceDir)) { + new File(deletedPackageAppInfo.publicSourceDir).delete(); + } + //update signature on the new package setting + //this should always succeed, since we checked the + //signature earlier. + synchronized(mPackages) { + verifySignaturesLP(mSettings.mPackages.get(pkgName), pkg, + parseFlags, true); + } + } else { + // remove package from internal structures. Note that we want deletePackageX to + // delete the package data and cache directories that it created in + // scanPackageLocked, unless those directories existed before we even tried to + // install. + if(updatedSettings) { + deletePackageLI( + pkgName, true, + PackageManager.DONT_DELETE_DATA, + res.removedInfo); + } + // Since we failed to install the new package we need to restore the old + // package that we deleted. + if(deletedPkg) { + installPackageLI( + Uri.fromFile(new File(deletedPackage.mPath)), + isForwardLocked(deletedPackage) + ? PackageManager.FORWARD_LOCK_PACKAGE + : 0); + } + } + } + + private void replaceSystemPackageLI(PackageParser.Package deletedPackage, int parseFlags, + File tmpPackageFile, + String destFilePath, File destPackageFile, File destResourceFile, + PackageParser.Package pkg, boolean forwardLocked, + PackageInstalledInfo res) { + PackageParser.Package newPackage = null; + boolean updatedSettings = false; + parseFlags |= PackageParser.PARSE_IS_SYSTEM; + String packageName = deletedPackage.packageName; + res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; + if (packageName == null) { + Log.w(TAG, "Attempt to delete null packageName."); + return; + } + PackageParser.Package oldPkg; + PackageSetting oldPkgSetting; + synchronized (mPackages) { + oldPkg = mPackages.get(packageName); + oldPkgSetting = mSettings.mPackages.get(packageName); + if((oldPkg == null) || (oldPkg.applicationInfo == null) || + (oldPkgSetting == null)) { + Log.w(TAG, "Could'nt find package:"+packageName+" information"); + return; + } + } + res.removedInfo.uid = oldPkg.applicationInfo.uid; + res.removedInfo.removedPackage = packageName; + // Remove existing system package + removePackageLI(oldPkg, true); + synchronized (mPackages) { + res.removedInfo.removedUid = mSettings.disableSystemPackageLP(packageName); + } + + // Successfully disabled the old package. Now proceed with re-installation + mLastScanError = PackageManager.INSTALL_SUCCEEDED; + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + newPackage = scanPackageLI(tmpPackageFile, destPackageFile, + destResourceFile, pkg, parseFlags, + SCAN_MONITOR | SCAN_FORCE_DEX + | SCAN_UPDATE_SIGNATURE + | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)); + if (newPackage == null) { + Log.w(TAG, "Package couldn't be installed in " + destPackageFile); + if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; + } + } else { + updateSettingsLI(packageName, tmpPackageFile, + destFilePath, destPackageFile, + destResourceFile, pkg, + newPackage, + true, + forwardLocked, + res); + updatedSettings = true; + } + + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + //update signature on the new package setting + //this should always succeed, since we checked the + //signature earlier. + synchronized(mPackages) { + verifySignaturesLP(mSettings.mPackages.get(packageName), pkg, + parseFlags, true); + } + } else { + // Re installation failed. Restore old information + // Remove new pkg information + removePackageLI(newPackage, true); + // Add back the old system package + scanPackageLI(oldPkgSetting.codePath, oldPkgSetting.codePath, + oldPkgSetting.resourcePath, + oldPkg, parseFlags, + SCAN_MONITOR + | SCAN_UPDATE_SIGNATURE + | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)); + // Restore the old system information in Settings + synchronized(mPackages) { + if(updatedSettings) { + mSettings.enableSystemPackageLP(packageName); + } + mSettings.writeLP(); + } + } + } + + private void updateSettingsLI(String pkgName, File tmpPackageFile, + String destFilePath, File destPackageFile, + File destResourceFile, + PackageParser.Package pkg, + PackageParser.Package newPackage, + boolean replacingExistingPackage, + boolean forwardLocked, + PackageInstalledInfo res) { + synchronized (mPackages) { + //write settings. the installStatus will be incomplete at this stage. + //note that the new package setting would have already been + //added to mPackages. It hasn't been persisted yet. + mSettings.setInstallStatus(pkgName, PKG_INSTALL_INCOMPLETE); + mSettings.writeLP(); + } + + int retCode = 0; + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { + retCode = mInstaller.movedex(tmpPackageFile.toString(), + destPackageFile.toString()); + if (retCode != 0) { + Log.e(TAG, "Couldn't rename dex file: " + destPackageFile); + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return; + } + } + // XXX There are probably some big issues here: upon doing + // the rename, we have reached the point of no return (the + // original .apk is gone!), so we can't fail. Yet... we can. + if (!tmpPackageFile.renameTo(destPackageFile)) { + Log.e(TAG, "Couldn't move package file to: " + destPackageFile); + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } else { + res.returnCode = setPermissionsLI(pkgName, newPackage, destFilePath, + destResourceFile, + forwardLocked); + if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) { + return; + } else { + Log.d(TAG, "New package installed in " + destPackageFile); + } + } + if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) { + if (mInstaller != null) { + mInstaller.rmdex(tmpPackageFile.getPath()); + } + } + + synchronized (mPackages) { + grantPermissionsLP(newPackage, true); + res.name = pkgName; + res.uid = newPackage.applicationInfo.uid; + res.pkg = newPackage; + mSettings.setInstallStatus(pkgName, PKG_INSTALL_COMPLETE); + res.returnCode = PackageManager.INSTALL_SUCCEEDED; + //to update install status + mSettings.writeLP(); + } + } + + private PackageInstalledInfo installPackageLI(Uri pPackageURI, int pFlags) { File tmpPackageFile = null; - boolean wroteSettings = false; String pkgName = null; - PackageParser.Package newPackage = null; - boolean dataDirExists = false; - PackageParser.Package deletedPackage = null; - PackageRemovedInfo removedInfo = new PackageRemovedInfo(); + boolean forwardLocked = false; + boolean replacingExistingPackage = false; + + // Result object to be returned + PackageInstalledInfo res = new PackageInstalledInfo(); + res.returnCode = PackageManager.INSTALL_SUCCEEDED; + res.uid = -1; + res.pkg = null; + res.removedInfo = new PackageRemovedInfo(); main_flow: try { tmpPackageFile = createTempPackageFile(); if (tmpPackageFile == null) { - returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; break main_flow; } tmpPackageFile.deleteOnExit(); // paranoia - if (packageURI.getScheme().equals("file")) { - final File srcPackageFile = new File(packageURI.getPath()); + if (pPackageURI.getScheme().equals("file")) { + final File srcPackageFile = new File(pPackageURI.getPath()); // We copy the source package file to a temp file and then rename it to the // destination file in order to eliminate a window where the package directory // scanner notices the new package file but it's not completely copied yet. if (!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) { Log.e(TAG, "Couldn't copy package file to temp file."); - returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; break main_flow; } - } else if (packageURI.getScheme().equals("content")) { + } else if (pPackageURI.getScheme().equals("content")) { ParcelFileDescriptor fd; try { - fd = mContext.getContentResolver().openFileDescriptor(packageURI, "r"); + fd = mContext.getContentResolver().openFileDescriptor(pPackageURI, "r"); } catch (FileNotFoundException e) { Log.e(TAG, "Couldn't open file descriptor from download service."); - returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; break main_flow; } if (fd == null) { Log.e(TAG, "Couldn't open file descriptor from download service (null)."); - returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; break main_flow; } if (Config.LOGV) { @@ -3091,36 +3520,38 @@ class PackageManagerService extends IPackageManager.Stub { // scanner notices the new package file but it's not completely copied yet. if (!FileUtils.copyToFile(dlStream, tmpPackageFile)) { Log.e(TAG, "Couldn't copy package stream to temp file."); - returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; break main_flow; } } else { - Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + packageURI); - returnCode = PackageManager.INSTALL_FAILED_INVALID_URI; + Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_URI; break main_flow; - } + } pkgName = PackageParser.parsePackageName( tmpPackageFile.getAbsolutePath(), 0); if (pkgName == null) { Log.e(TAG, "Couldn't find a package name in : " + tmpPackageFile); - returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; break main_flow; } + res.name = pkgName; //initialize some variables before installing pkg final String pkgFileName = pkgName + ".apk"; - final File destDir = ((flags&PackageManager.FORWARD_LOCK_PACKAGE) != 0) + final File destDir = ((pFlags&PackageManager.FORWARD_LOCK_PACKAGE) != 0) ? mDrmAppPrivateInstallDir : mAppInstallDir; final File destPackageFile = new File(destDir, pkgFileName); final String destFilePath = destPackageFile.getAbsolutePath(); File destResourceFile; - if ((flags&PackageManager.FORWARD_LOCK_PACKAGE) != 0) { + if ((pFlags&PackageManager.FORWARD_LOCK_PACKAGE) != 0) { final String publicZipFileName = pkgName + ".zip"; destResourceFile = new File(mAppInstallDir, publicZipFileName); + forwardLocked = true; } else { destResourceFile = destPackageFile; } - //retrieve PackageSettings and parse package + // Retrieve PackageSettings and parse package int parseFlags = PackageParser.PARSE_CHATTY; parseFlags |= mDefParseFlags; PackageParser pp = new PackageParser(tmpPackageFile.getPath()); @@ -3129,204 +3560,79 @@ class PackageManagerService extends IPackageManager.Stub { final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, destPackageFile.getAbsolutePath(), mMetrics, parseFlags); if (pkg == null) { - returnCode = pp.getParseError(); + res.returnCode = pp.getParseError(); break main_flow; } if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) { - returnCode = pp.getParseError(); + res.returnCode = pp.getParseError(); break main_flow; } - boolean replacingExistingPackage = false; - synchronized (mPackages) { //check if installing already existing package - if ((flags&PackageManager.REPLACE_EXISTING_PACKAGE) != 0 + if ((pFlags&PackageManager.REPLACE_EXISTING_PACKAGE) != 0 && mPackages.containsKey(pkgName)) { replacingExistingPackage = true; - deletedPackage = mPackages.get(pkgName); - if(checkSignaturesLP(pkg, deletedPackage) != PackageManager.SIGNATURE_MATCH) { - returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; - break main_flow; - } - } - } - - if (replacingExistingPackage) { - if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA, - removedInfo)) { - returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; - break main_flow; - } - } else { - if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(destFilePath)) { - // Don't allow installation over an existing package with the same name. - Log.w(TAG, "Attempt to re-install " + pkgName - + " without first uninstalling."); - returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; - break main_flow; - } - if (destPackageFile.exists()) { - // It's safe to do this because we know (from the above check) that the file - // isn't currently used for an installed package. - destPackageFile.delete(); - } - } - - // Remember this for later, in case we need to rollback this install - dataDirExists = (new File(mAppDataDir, pkgName)).exists(); - mLastScanError = PackageManager.INSTALL_SUCCEEDED; - newPackage = scanPackageLI(tmpPackageFile, destPackageFile, - destResourceFile, pkg, parseFlags, - SCAN_MONITOR | SCAN_FORCE_DEX - | (!replacingExistingPackage ? SCAN_UPDATE_SIGNATURE : 0) - | ((flags&PackageManager.FORWARD_LOCK_PACKAGE) != 0 - ? SCAN_FORWARD_LOCKED : 0)); - if (newPackage == null) { - if (Config.LOGD) { - Log.w(TAG, "Package couldn't be installed in " + destPackageFile); - } - if ((returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { - returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; } - break main_flow; } - synchronized (mPackages) { - //write settings. the installStatus will be incomplete at this stage. - //note that the new package setting would have already been - //added to mPackages. It hasn't been persisted yet. - mSettings.setInstallStatus(pkgName, PKG_INSTALL_INCOMPLETE); - mSettings.writeLP(); - wroteSettings = true; - } - - int retCode = 0; - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - retCode = mInstaller.movedex(tmpPackageFile.toString(), - destPackageFile.toString()); - if (retCode != 0) { - Log.e(TAG, "Couldn't rename dex file: " + destPackageFile); - returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - break main_flow; - } - } - // XXX There are probably some big issues here: upon doing - // the rename, we have reached the point of no return (the - // original .apk is gone!), so we can't fail. Yet... we can. - if (tmpPackageFile.renameTo(destPackageFile)) { - if ((flags&PackageManager.FORWARD_LOCK_PACKAGE) != 0) { - try { - extractPublicFiles(newPackage, destResourceFile); - } catch (IOException e) { - Log.e(TAG, "Couldn't create a new zip file for the public parts of a" + - " forward-locked app."); - returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - break main_flow; - } - if (mInstaller != null) { - retCode = mInstaller.setForwardLockPerm(pkgName, - newPackage.applicationInfo.uid); - } else { - final int filePermissions = - FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP; - retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, - newPackage.applicationInfo.uid); - } - } else { - final int filePermissions = - FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP - |FileUtils.S_IROTH; - retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, -1); - } - if (retCode != 0) { - Log.e(TAG, "Couldn't set new package file permissions for " + destFilePath - + ". The return code was: " + retCode); - } - if (Config.LOGD) { - Log.d(TAG, "New package installed in " + destPackageFile); - } - - synchronized (mPackages) { - grantPermissionsLP(newPackage, true); - installedPackageName = pkgName; - installedPackageUid = newPackage.applicationInfo.uid; - installedPackage = newPackage; - if(replacingExistingPackage) { - //update signature on the new package setting - //this should always succeed, since we checked the - //signature earlier. - verifySignaturesLP(mSettings.mPackages.get(pkgName), pkg, - parseFlags, true); - } - mSettings.setInstallStatus(pkgName, PKG_INSTALL_COMPLETE); - returnCode = PackageManager.INSTALL_SUCCEEDED; - //to update install status - mSettings.writeLP(); - break main_flow; - } + if(replacingExistingPackage) { + replacePackageLI(pkgName, pFlags, + tmpPackageFile, + destFilePath, destPackageFile, destResourceFile, + pkg, forwardLocked, + res); } else { - Log.e(TAG, "Couldn't move package file to: " + destPackageFile); - returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - break main_flow; + installNewPackageLI(pkgName, pFlags, + tmpPackageFile, + destFilePath, destPackageFile, destResourceFile, + pkg, forwardLocked, + res); } } finally { if (tmpPackageFile != null && tmpPackageFile.exists()) { tmpPackageFile.delete(); } - - //if install was successful the pkg's installStatus would have - //been updated and hence needs to be persisted. if not - //the pkg info needs to be cleaned up. - if (returnCode == PackageManager.INSTALL_SUCCEEDED) { - if (deletedPackage != null) { - // If we deleted an exisiting package, the old source and resource files that we - // were keeping around in case we needed them (see below) can now be deleted - final ApplicationInfo deletedPackageAppInfo = deletedPackage.applicationInfo; - final ApplicationInfo installedPackageAppInfo = - installedPackage.applicationInfo; - if (!deletedPackageAppInfo.sourceDir - .equals(installedPackageAppInfo.sourceDir)) { - new File(deletedPackageAppInfo.sourceDir).delete(); - } - if (!deletedPackageAppInfo.publicSourceDir - .equals(installedPackageAppInfo.publicSourceDir)) { - new File(deletedPackageAppInfo.publicSourceDir).delete(); - } - } + return res; + } + } + + private int setPermissionsLI(String pkgName, + PackageParser.Package newPackage, + String destFilePath, + File destResourceFile, + boolean forwardLocked) { + int retCode; + if (forwardLocked) { + try { + extractPublicFiles(newPackage, destResourceFile); + } catch (IOException e) { + Log.e(TAG, "Couldn't create a new zip file for the public parts of a" + + " forward-locked app."); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } finally { + //TODO clean up the extracted public files + } + if (mInstaller != null) { + retCode = mInstaller.setForwardLockPerm(pkgName, + newPackage.applicationInfo.uid); } else { - if (mInstaller != null) { - mInstaller.rmdex(tmpPackageFile.getPath()); - } - if (wroteSettings) { - // remove package from internal structures. Note that we want deletePackageX to - // delete the package data and cache directories that it created in - // scanPackageLocked, unless those directories existed before we even tried to - // install. - deletePackageLI( - pkgName, true, - dataDirExists ? PackageManager.DONT_DELETE_DATA : 0, - new PackageRemovedInfo()); - if (deletedPackage != null) { - // Since we failed to install the new package we need to restore the old - // package that we deleted. - installPackageLI( - Uri.fromFile(new File(deletedPackage.mPath)), - isForwardLocked(deletedPackage) - ? PackageManager.FORWARD_LOCK_PACKAGE - : 0); - } - } + final int filePermissions = + FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP; + retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, + newPackage.applicationInfo.uid); } + } else { + final int filePermissions = + FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP + |FileUtils.S_IROTH; + retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, -1); } - - PackageInstalledInfo res = new PackageInstalledInfo(); - res.name = installedPackageName; - res.uid = installedPackageUid; - res.pkg = installedPackage; - res.returnCode = returnCode; - res.removedInfo = removedInfo; - return res; + if (retCode != 0) { + Log.e(TAG, "Couldn't set new package file permissions for " + destFilePath + + ". The return code was: " + retCode); + } + return PackageManager.INSTALL_SUCCEEDED; } private boolean isForwardLocked(PackageParser.Package deletedPackage) { @@ -3470,11 +3776,6 @@ class PackageManagerService extends IPackageManager.Stub { synchronized (mInstallLock) { res = deletePackageLI(packageName, deleteCodeAndResources, flags, info); - if (res) { - synchronized (mPackages) { - mSettings.writeLP(); - } - } } if(res && sendBroadCast) { @@ -3500,44 +3801,117 @@ class PackageManagerService extends IPackageManager.Stub { } } - private boolean deletePackageLI(String packageName, + /* + * This method deletes the package from internal data structures. If the DONT_DELETE_DATA + * flag is not set, the data directory is removed as well. + * make sure this flag is set for partially installed apps. If not its meaningless to + * delete a partially installed application. + */ + private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo, + int flags) { + String packageName = p.packageName; + outInfo.removedPackage = packageName; + removePackageLI(p, true); + // Retrieve object to delete permissions for shared user later on + PackageSetting deletedPs; + synchronized (mPackages) { + deletedPs = mSettings.mPackages.get(packageName); + } + if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { + if (mInstaller != null) { + int retCode = mInstaller.remove(packageName); + if (retCode < 0) { + Log.w(TAG, "Couldn't remove app data or cache directory for package: " + + packageName + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion + } + } else { + //for emulator + PackageParser.Package pkg = mPackages.get(packageName); + File dataDir = new File(pkg.applicationInfo.dataDir); + dataDir.delete(); + } + synchronized (mPackages) { + outInfo.removedUid = mSettings.removePackageLP(packageName); + } + } + synchronized (mPackages) { + if ( (deletedPs != null) && (deletedPs.sharedUser != null)) { + // remove permissions associated with package + mSettings.updateSharedUserPerms (deletedPs); + } + // Save settings now + mSettings.writeLP (); + } + } + + /* + * Tries to delete system package. + */ + private boolean deleteSystemPackageLI(PackageParser.Package p, boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) { - if (packageName == null) { - Log.w(TAG, "Attempt to delete null packageName."); + ApplicationInfo applicationInfo = p.applicationInfo; + //applicable for non-partially installed applications only + if (applicationInfo == null) { + Log.w(TAG, "Package " + p.packageName + " has no applicationInfo."); return false; } - PackageParser.Package p; + PackageSetting ps = null; + // Confirm if the system package has been updated + // An updated system app can be deleted. This will also have to restore + // the system pkg from system partition synchronized (mPackages) { - p = mPackages.get(packageName); + ps = mSettings.getDisabledSystemPkg(p.packageName); } - if (p == null) { - Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + if (ps == null) { + Log.w(TAG, "Attempt to delete system package "+ p.packageName); return false; + } else { + Log.i(TAG, "Deleting system pkg from data partition"); } - final ApplicationInfo applicationInfo = p.applicationInfo; - if (applicationInfo == null) { - Log.w(TAG, "Package " + packageName + " has no applicationInfo."); + // Delete the updated package + boolean ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo); + if (!ret) { return false; } - - // NB: This is a public nonfinal boolean so it theoretically - // could be altered by anyone. In practice, that can only be - // done from within this same process, and anyway file - // permissions will prevent unauthorized deletion of system - // packages. - if ((applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { - Log.w(TAG, "Attempt to delete system package "+ packageName); + synchronized (mPackages) { + // Reinstate the old system package + mSettings.enableSystemPackageLP(p.packageName); + } + // Install the system package + PackageParser.Package newPkg = scanPackageLI(ps.codePath, ps.codePath, ps.resourcePath, + PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM, + SCAN_MONITOR); + + if (newPkg == null) { + Log.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError); return false; } - final File sourceFile = new File(applicationInfo.sourceDir); + synchronized (mPackages) { + mSettings.writeLP(); + } + return true; + } + + private boolean deleteInstalledPackageLI(PackageParser.Package p, + boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) { + ApplicationInfo applicationInfo = p.applicationInfo; + if (applicationInfo == null) { + Log.w(TAG, "Package " + p.packageName + " has no applicationInfo."); + return false; + } + // Delete application's source directory + File sourceFile = new File(applicationInfo.sourceDir); if (!sourceFile.exists()) { Log.w(TAG, "Package source " + applicationInfo.sourceDir + " does not exist."); return false; } - outInfo.uid = p.applicationInfo.uid; + outInfo.uid = applicationInfo.uid; - outInfo.removedPackage = packageName; - removePackageLI(p, true); + // Delete package data from internal structures and also remove data if flag is set + removePackageDataLI(p, outInfo, flags); + + // Delete application code and resources if (deleteCodeAndResources) { sourceFile.delete(); final File publicSourceFile = new File(applicationInfo.publicSourceDir); @@ -3548,30 +3922,61 @@ class PackageManagerService extends IPackageManager.Stub { int retCode = mInstaller.rmdex(sourceFile.toString()); if (retCode < 0) { Log.w(TAG, "Couldn't remove dex file for package: " - + packageName + ", retcode=" + retCode); + + p.packageName + " at location " + sourceFile.toString() + ", retcode=" + retCode); // we don't consider this to be a failure of the core package deletion } } } - if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { - if (mInstaller != null) { - int retCode = mInstaller.remove(packageName); - if (retCode < 0) { - Log.w(TAG, "Couldn't remove app data or cache directory for package: " - + packageName + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion + return true; + } + + /* + * This method handles package deletion in general + */ + private boolean deletePackageLI(String packageName, + boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) { + if (packageName == null) { + Log.w(TAG, "Attempt to delete null packageName."); + return false; + } + PackageParser.Package p; + boolean dataOnly = false; + synchronized (mPackages) { + p = mPackages.get(packageName); + if (p == null) { + //this retrieves partially installed apps + dataOnly = true; + PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps == null) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; } - } else { - //for emulator - PackageParser.Package pkg = mPackages.get(packageName); - File dataDir = new File(pkg.applicationInfo.dataDir); - dataDir.delete(); - } - synchronized (mPackages) { - outInfo.removedUid = mSettings.removePackageLP(packageName); + p = ps.pkg; } } - return true; + if (p == null) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + + if (dataOnly) { + // Delete application data first + removePackageDataLI(p, outInfo, flags); + return true; + } + // At this point the package should have ApplicationInfo associated with it + if (p.applicationInfo == null) { + Log.w(TAG, "Package " + p.packageName + " has no applicationInfo."); + return false; + } + if ( (p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + Log.i(TAG, "Removing system package:"+p.packageName); + // When an updated system application is deleted we delete the existing resources as well and + // fall back to existing code in system partition + return deleteSystemPackageLI(p, true, flags, outInfo); + } + Log.i(TAG, "Removing non-system package:"+p.packageName); + return deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo); } public void clearApplicationUserData(final String packageName, @@ -3582,21 +3987,21 @@ class PackageManagerService extends IPackageManager.Stub { mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); - final boolean succeded; + final boolean succeeded; synchronized (mInstallLock) { - succeded = clearApplicationUserDataLI(packageName); - } - if(succeded) { - //invoke DeviceMemoryMonitor's update method to clear any notifications - DeviceMemoryMonitorService dmm = (DeviceMemoryMonitorService) - ServiceManager.getService("devicememorymonitor"); - if(dmm!=null) { - dmm.updateMemory(); + succeeded = clearApplicationUserDataLI(packageName); + } + if (succeeded) { + // invoke DeviceStorageMonitor's update method to clear any notifications + DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) + ServiceManager.getService(DeviceStorageMonitorService.SERVICE); + if (dsm != null) { + dsm.updateMemory(); } } if(observer != null) { try { - observer.onRemoveCompleted(packageName, succeded); + observer.onRemoveCompleted(packageName, succeeded); } catch (RemoteException e) { Log.i(TAG, "Observer no longer exists."); } @@ -3611,23 +4016,36 @@ class PackageManagerService extends IPackageManager.Stub { return false; } PackageParser.Package p; + boolean dataOnly = false; synchronized (mPackages) { p = mPackages.get(packageName); + if(p == null) { + dataOnly = true; + PackageSetting ps = mSettings.mPackages.get(packageName); + if((ps == null) || (ps.pkg == null)) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + p = ps.pkg; + } } - if (p == null) { - Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); - return false; - } - final ApplicationInfo applicationInfo = p.applicationInfo; - if (applicationInfo == null) { - Log.w(TAG, "Package " + packageName + " has no applicationInfo."); - return false; + if(!dataOnly) { + //need to check this only for fully installed applications + if (p == null) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + final ApplicationInfo applicationInfo = p.applicationInfo; + if (applicationInfo == null) { + Log.w(TAG, "Package " + packageName + " has no applicationInfo."); + return false; + } } if (mInstaller != null) { int retCode = mInstaller.clearUserData(packageName); if (retCode < 0) { Log.w(TAG, "Couldn't remove cache files for package: " - + packageName); + + packageName); return false; } } @@ -3716,22 +4134,31 @@ class PackageManagerService extends IPackageManager.Stub { return false; } PackageParser.Package p; + boolean dataOnly = false; synchronized (mPackages) { p = mPackages.get(packageName); + if(p == null) { + dataOnly = true; + PackageSetting ps = mSettings.mPackages.get(packageName); + if((ps == null) || (ps.pkg == null)) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + p = ps.pkg; + } } - if (p == null) { - Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); - return false; - } - final ApplicationInfo applicationInfo = p.applicationInfo; - if (applicationInfo == null) { - Log.w(TAG, "Package " + packageName + " has no applicationInfo."); - return false; + String publicSrcDir = null; + if(!dataOnly) { + final ApplicationInfo applicationInfo = p.applicationInfo; + if (applicationInfo == null) { + Log.w(TAG, "Package " + packageName + " has no applicationInfo."); + return false; + } + publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null; } if (mInstaller != null) { int res = mInstaller.getSizeInfo(packageName, p.mPath, - isForwardLocked(p) ? applicationInfo.publicSourceDir : null, - pStats); + publicSrcDir, pStats); if (res < 0) { return false; } else { @@ -4051,7 +4478,7 @@ class PackageManagerService extends IPackageManager.Stub { if (ps.pkg != null) { pw.println(" dataDir=" + ps.pkg.applicationInfo.dataDir); } - pw.println(" timeStamp=" + ps.timeStamp); + pw.println(" timeStamp=" + ps.getTimeStampStr()); pw.println(" signatures=" + ps.signatures); pw.println(" permissionsFixed=" + ps.permissionsFixed + " pkgFlags=0x" + Integer.toHexString(ps.pkgFlags) @@ -4569,8 +4996,8 @@ class PackageManagerService extends IPackageManager.Stub { final String codePathString; final File resourcePath; final String resourcePathString; - long timeStamp; - String timeStampString = "0"; + private long timeStamp; + private String timeStampString = "0"; PackageSignatures signatures = new PackageSignatures(); @@ -4612,6 +5039,14 @@ class PackageManagerService extends IPackageManager.Stub { timeStamp = newStamp; timeStampString = newStampStr; } + + public long getTimeStamp() { + return timeStamp; + } + + public String getTimeStampStr() { + return timeStampString; + } public void copyFrom(PackageSettingBase base) { grantedPermissions = base.grantedPermissions; @@ -4709,6 +5144,10 @@ class PackageManagerService extends IPackageManager.Stub { // First is the most preferred. private final ArrayList<PackageSetting> mPreferredPackages = new ArrayList<PackageSetting>(); + // List of replaced system applications + final HashMap<String, PackageSetting> mDisabledSysPackages = + new HashMap<String, PackageSetting>(); + // The user's preferred activities associated with particular intent // filters. private final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities = @@ -4781,6 +5220,7 @@ class PackageManagerService extends IPackageManager.Stub { final String name = pkg.packageName; PackageSetting p = getPackageLP(name, sharedUser, codePath, resourcePath, pkgFlags, create); + if (p != null) { p.pkg = pkg; } @@ -4836,6 +5276,39 @@ class PackageManagerService extends IPackageManager.Stub { return s; } + int disableSystemPackageLP(String name) { + PackageSetting p = mPackages.get(name); + if(p == null) { + Log.w(TAG, "Package:"+name+" is not an installed package"); + return -1; + } + PackageSetting dp = mDisabledSysPackages.get(name); + // always make sure the system package code and resource paths dont change + if(dp == null) { + if((p.pkg != null) && (p.pkg.applicationInfo != null)) { + p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + } + mDisabledSysPackages.put(name, p); + } + return removePackageLP(name); + } + + PackageSetting enableSystemPackageLP(String name) { + PackageSetting p = mDisabledSysPackages.get(name); + if(p == null) { + Log.w(TAG, "Package:"+name+" is not disabled"); + return null; + } + // Reset flag in ApplicationInfo object + if((p.pkg != null) && (p.pkg.applicationInfo != null)) { + p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + } + PackageSetting ret = addPackageLP(name, p.codePath, + p.resourcePath, p.userId, p.pkgFlags); + mDisabledSysPackages.remove(name); + return ret; + } + PackageSetting addPackageLP(String name, File codePath, File resourcePath, int uid, int pkgFlags) { PackageSetting p = mPackages.get(name); @@ -4881,10 +5354,22 @@ class PackageManagerService extends IPackageManager.Stub { PackageSetting p = mPackages.get(name); if (p != null) { if (!p.codePath.equals(codePath)) { - reportSettingsProblem(Log.WARN, - "Package " + name + " codePath changed from " + p.codePath - + " to " + codePath + "; replacing with new"); - p = null; + // Check to see if its a disabled system app + PackageSetting ps = mDisabledSysPackages.get(name); + if((ps != null) && ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0)) { + // Could be a replaced system package + // Note that if the user replaced a system app, the user has to physically + // delete the new one in order to revert to the system app. So even + // if the user updated the system app via an update, the user still + // has to delete the one installed in the data partition in order to pick up the + // new system package. + return p; + } else { + reportSettingsProblem(Log.WARN, + "Package " + name + " codePath changed from " + p.codePath + + " to " + codePath + "; replacing with new"); + p = null; + } } else if (p.sharedUser != sharedUser) { reportSettingsProblem(Log.WARN, "Package " + name + " shared user changed from " @@ -4896,10 +5381,13 @@ class PackageManagerService extends IPackageManager.Stub { } } if (p == null) { + // Create a new PackageSettings entry. this can end up here because + // of code path mismatch or user id mismatch of an updated system partition if (!create) { return null; } p = new PackageSetting(name, codePath, resourcePath, pkgFlags); + p.setTimeStamp(codePath.lastModified()); if (sharedUser != null) { p.userId = sharedUser.userId; } else if (MULTIPLE_APPLICATION_UIDS) { @@ -4935,10 +5423,45 @@ class PackageManagerService extends IPackageManager.Stub { p.sharedUser = sharedUser; p.userId = sharedUser.userId; } - return p; } + private void updateSharedUserPerms (PackageSetting deletedPs) { + if ( (deletedPs == null) || (deletedPs.pkg == null)) { + Log.i(TAG, "Trying to update info for null package. Just ignoring"); + return; + } + // No sharedUserId + if (deletedPs.sharedUser == null) { + return; + } + SharedUserSetting sus = deletedPs.sharedUser; + // Update permissions + for (String eachPerm: deletedPs.pkg.requestedPermissions) { + boolean used = false; + if (!sus.grantedPermissions.contains (eachPerm)) { + continue; + } + for (PackageSetting pkg:sus.packages) { + if (pkg.grantedPermissions.contains (eachPerm)) { + used = true; + break; + } + } + if (!used) { + // can safely delete this permission from list + sus.grantedPermissions.remove(eachPerm); + sus.loadedPermissions.remove(eachPerm); + } + } + // Update gids + int newGids[] = null; + for (PackageSetting pkg:sus.packages) { + newGids = appendInts(newGids, pkg.gids); + } + sus.gids = newGids; + } + private int removePackageLP(String name) { PackageSetting p = mPackages.get(name); if (p != null) { @@ -5047,16 +5570,17 @@ class PackageManagerService extends IPackageManager.Stub { serializer.endTag(null, "permissions"); for (PackageSetting pkg : mPackages.values()) { - serializer.startTag(null, "package"); + writePackage(serializer, pkg); + } + + for (PackageSetting pkg : mDisabledSysPackages.values()) { + serializer.startTag(null, "updated-package"); serializer.attribute(null, "name", pkg.name); serializer.attribute(null, "codePath", pkg.codePathString); + serializer.attribute(null, "ts", pkg.getTimeStampStr()); if (!pkg.resourcePathString.equals(pkg.codePathString)) { serializer.attribute(null, "resourcePath", pkg.resourcePathString); } - serializer.attribute(null, "system", - (pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 - ? "true" : "false"); - serializer.attribute(null, "ts", pkg.timeStampString); if (pkg.sharedUser == null) { serializer.attribute(null, "userId", Integer.toString(pkg.userId)); @@ -5064,50 +5588,9 @@ class PackageManagerService extends IPackageManager.Stub { serializer.attribute(null, "sharedUserId", Integer.toString(pkg.userId)); } - if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { - serializer.attribute(null, "enabled", - pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED - ? "true" : "false"); - } - if(pkg.installStatus == PKG_INSTALL_INCOMPLETE) { - serializer.attribute(null, "installStatus", "false"); - } - pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); - if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { - serializer.startTag(null, "perms"); - if (pkg.sharedUser == null) { - // If this is a shared user, the permissions will - // be written there. We still need to write an - // empty permissions list so permissionsFixed will - // be set. - for (String name : pkg.grantedPermissions) { - serializer.startTag(null, "item"); - serializer.attribute(null, "name", name); - serializer.endTag(null, "item"); - } - } - serializer.endTag(null, "perms"); - } - if (pkg.disabledComponents.size() > 0) { - serializer.startTag(null, "disabled-components"); - for (String name : pkg.disabledComponents) { - serializer.startTag(null, "item"); - serializer.attribute(null, "name", name); - serializer.endTag(null, "item"); - } - serializer.endTag(null, "disabled-components"); - } - if (pkg.enabledComponents.size() > 0) { - serializer.startTag(null, "enabled-components"); - for (String name : pkg.enabledComponents) { - serializer.startTag(null, "item"); - serializer.attribute(null, "name", name); - serializer.endTag(null, "item"); - } - serializer.endTag(null, "enabled-components"); - } - serializer.endTag(null, "package"); + serializer.endTag(null, "updated-package"); } + serializer.startTag(null, "preferred-packages"); int N = mPreferredPackages.size(); @@ -5169,7 +5652,71 @@ class PackageManagerService extends IPackageManager.Stub { //Debug.stopMethodTracing(); } - + + void writePackage(XmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { + serializer.startTag(null, "package"); + serializer.attribute(null, "name", pkg.name); + serializer.attribute(null, "codePath", pkg.codePathString); + if (!pkg.resourcePathString.equals(pkg.codePathString)) { + serializer.attribute(null, "resourcePath", pkg.resourcePathString); + } + serializer.attribute(null, "system", + (pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 + ? "true" : "false"); + serializer.attribute(null, "ts", pkg.getTimeStampStr()); + if (pkg.sharedUser == null) { + serializer.attribute(null, "userId", + Integer.toString(pkg.userId)); + } else { + serializer.attribute(null, "sharedUserId", + Integer.toString(pkg.userId)); + } + if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { + serializer.attribute(null, "enabled", + pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED + ? "true" : "false"); + } + if(pkg.installStatus == PKG_INSTALL_INCOMPLETE) { + serializer.attribute(null, "installStatus", "false"); + } + pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); + if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { + serializer.startTag(null, "perms"); + if (pkg.sharedUser == null) { + // If this is a shared user, the permissions will + // be written there. We still need to write an + // empty permissions list so permissionsFixed will + // be set. + for (final String name : pkg.grantedPermissions) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + } + serializer.endTag(null, "perms"); + } + if (pkg.disabledComponents.size() > 0) { + serializer.startTag(null, "disabled-components"); + for (final String name : pkg.disabledComponents) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "disabled-components"); + } + if (pkg.enabledComponents.size() > 0) { + serializer.startTag(null, "enabled-components"); + for (final String name : pkg.enabledComponents) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "enabled-components"); + } + serializer.endTag(null, "package"); + } + void writePermission(XmlSerializer serializer, BasePermission bp) throws XmlPullParserException, java.io.IOException { if (bp.type != BasePermission.TYPE_BUILTIN @@ -5281,6 +5828,34 @@ class PackageManagerService extends IPackageManager.Stub { readPreferredPackagesLP(parser); } else if (tagName.equals("preferred-activities")) { readPreferredActivitiesLP(parser); + } else if(tagName.equals("updated-package")) { + String name = parser.getAttributeValue(null, "name"); + String codePathStr = parser.getAttributeValue(null, "codePath"); + String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); + if(resourcePathStr == null) { + resourcePathStr = codePathStr; + } + + int pkgFlags = 0; + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + PackageSetting ps = new PackageSetting(name, + new File(codePathStr), + new File(resourcePathStr), pkgFlags); + String timeStampStr = parser.getAttributeValue(null, "ts"); + if (timeStampStr != null) { + try { + long timeStamp = Long.parseLong(timeStampStr); + ps.setTimeStamp(timeStamp, timeStampStr); + } catch (NumberFormatException e) { + } + } + String idStr = parser.getAttributeValue(null, "userId"); + ps.userId = idStr != null ? Integer.parseInt(idStr) : 0; + if(ps.userId <= 0) { + String sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); + ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; + } + mDisabledSysPackages.put(name, ps); } else { Log.w(TAG, "Unknown element under <packages>: " + parser.getName()); @@ -5811,6 +6386,13 @@ class PackageManagerService extends IPackageManager.Stub { mUserIds.add(obj); return FIRST_APPLICATION_UID + N; } + + public PackageSetting getDisabledSystemPkg(String name) { + synchronized(mPackages) { + PackageSetting ps = mDisabledSysPackages.get(name); + return ps; + } + } boolean isEnabledLP(ComponentInfo componentInfo, int flags) { final PackageSetting packageSettings = mPackages.get(componentInfo.packageName); diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 4ff0844..3fa2087 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -17,7 +17,7 @@ package com.android.server; import com.android.internal.app.IBatteryStats; -import com.android.server.am.BatteryStats; +import com.android.server.am.BatteryStatsService; import android.app.ActivityManagerNative; import android.app.IActivityManager; @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.database.Cursor; +import android.os.BatteryStats; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; @@ -74,11 +75,15 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage | PowerManager.FULL_WAKE_LOCK; // time since last state: time since last event: - private static final int SHORT_KEYLIGHT_DELAY = 6000; // t+6 sec + // The short keylight delay comes from Gservices; this is the default. + private static final int SHORT_KEYLIGHT_DELAY_DEFAULT = 6000; // t+6 sec private static final int MEDIUM_KEYLIGHT_DELAY = 15000; // t+15 sec private static final int LONG_KEYLIGHT_DELAY = 6000; // t+6 sec private static final int LONG_DIM_TIME = 7000; // t+N-5 sec + // Cached Gservices settings; see updateGservicesValues() + private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT; + // flags for setPowerState private static final int SCREEN_ON_BIT = 0x00000001; private static final int SCREEN_BRIGHT_BIT = 0x00000002; @@ -129,7 +134,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage private final int MY_UID; private boolean mDoneBooting = false; - private boolean mStayOnWhilePluggedIn; + private int mStayOnConditions = 0; private int mNotificationQueue = -1; private int mNotificationWhy; private int mPartialCount = 0; @@ -163,7 +168,6 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage = new BrightnessState(Power.KEYBOARD_LIGHT); private final BrightnessState mButtonBrightness = new BrightnessState(Power.BUTTON_LIGHT); - private ContentResolver mContentResolver; private boolean mIsPowered = false; private IActivityManager mActivityService; private IBatteryStats mBatteryStats; @@ -285,10 +289,20 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } - public void setStayOnSetting(boolean val) { + /** + * Set the setting that determines whether the device stays on when plugged in. + * The argument is a bit string, with each bit specifying a power source that, + * when the device is connected to that source, causes the device to stay on. + * See {@link android.os.BatteryManager} for the list of power sources that + * can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC} + * and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB} + * @param val an {@code int} containing the bits that specify which power sources + * should cause the device to stay on. + */ + public void setStayOnSetting(int val) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null); Settings.System.putInt(mContext.getContentResolver(), - Settings.System.STAY_ON_WHILE_PLUGGED_IN, val ? 1 : 0); + Settings.System.STAY_ON_WHILE_PLUGGED_IN, val); } private class SettingsObserver implements Observer { @@ -299,7 +313,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage public void update(Observable o, Object arg) { synchronized (mLocks) { // STAY_ON_WHILE_PLUGGED_IN - mStayOnWhilePluggedIn = getInt(STAY_ON_WHILE_PLUGGED_IN) != 0; + mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN); updateWakeLockLocked(); // SCREEN_OFF_TIMEOUT @@ -337,7 +351,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage void init(Context context, IActivityManager activity, BatteryService battery) { mContext = context; mActivityService = activity; - mBatteryStats = BatteryStats.getService(); + mBatteryStats = BatteryStatsService.getService(); mBatteryService = battery; mHandlerThread = new HandlerThread("PowerManagerService") { @@ -376,10 +390,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); ContentResolver resolver = mContext.getContentResolver(); - mContentResolver = resolver; - - - Cursor settingsCursor = mContentResolver.query(Settings.System.CONTENT_URI, null, + Cursor settingsCursor = resolver.query(Settings.System.CONTENT_URI, null, "(" + Settings.System.NAME + "=?) or (" + Settings.System.NAME + "=?) or (" + Settings.System.NAME + "=?)", @@ -397,6 +408,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage filter.addAction(Intent.ACTION_BATTERY_CHANGED); mContext.registerReceiver(new BatteryReceiver(), filter); + // Listen for Gservices changes + IntentFilter gservicesChangedFilter = + new IntentFilter(Settings.Gservices.CHANGED_ACTION); + mContext.registerReceiver(new GservicesChangedReceiver(), gservicesChangedFilter); + // And explicitly do the initial update of our cached settings + updateGservicesValues(); + // turn everything on setPowerState(ALL_BRIGHT); @@ -444,7 +462,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } private void updateWakeLockLocked() { - if (mStayOnWhilePluggedIn && mBatteryService.isPowered()) { + if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) { // keep the device on if we're plugged in and mStayOnWhilePluggedIn is set. mStayOnWhilePluggedInScreenDimLock.acquire(); mStayOnWhilePluggedInPartialLock.acquire(); @@ -758,7 +776,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage pw.println(" mNextTimeout=" + mNextTimeout + " now=" + now + " " + ((mNextTimeout-now)/1000) + "s from now"); pw.println(" mDimScreen=" + mDimScreen - + " mStayOnWhilePluggedIn=" + mStayOnWhilePluggedIn); + + " mStayOnConditions=" + mStayOnConditions); pw.println(" mOffBecauseOfUser=" + mOffBecauseOfUser + " mUserState=" + mUserState); pw.println(" mNotificationQueue=" + mNotificationQueue @@ -1209,7 +1227,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage // was dim steps = (int)(ANIM_STEPS*ratio); } - if (mStayOnWhilePluggedIn && mBatteryService.isPowered()) { + if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) { // If the "stay on while plugged in" option is // turned on, then the screen will often not // automatically turn off while plugged in. To @@ -1362,7 +1380,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage private int getPreferredBrightness() { try { - final int brightness = Settings.System.getInt(mContentResolver, SCREEN_BRIGHTNESS); + final int brightness = Settings.System.getInt(mContext.getContentResolver(), + SCREEN_BRIGHTNESS); // Don't let applications turn the screen all the way off return Math.max(brightness, Power.BRIGHTNESS_DIM); } catch (SettingNotFoundException snfe) { @@ -1519,7 +1538,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage * */ private void setScreenOffTimeoutsLocked() { if ((mPokey & POKE_LOCK_SHORT_TIMEOUT) != 0) { - mKeylightDelay = SHORT_KEYLIGHT_DELAY; + mKeylightDelay = mShortKeylightDelay; // Configurable via Gservices mDimDelay = -1; mScreenOffDelay = 0; } else if ((mPokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0) { @@ -1553,6 +1572,31 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } + /** + * Refreshes cached Gservices settings. Called once on startup, and + * on subsequent Settings.Gservices.CHANGED_ACTION broadcasts (see + * GservicesChangedReceiver). + */ + private void updateGservicesValues() { + mShortKeylightDelay = Settings.Gservices.getInt( + mContext.getContentResolver(), + Settings.Gservices.SHORT_KEYLIGHT_DELAY_MS, + SHORT_KEYLIGHT_DELAY_DEFAULT); + // Log.i(TAG, "updateGservicesValues(): mShortKeylightDelay now " + mShortKeylightDelay); + } + + /** + * Receiver for the Gservices.CHANGED_ACTION broadcast intent, + * which tells us we need to refresh our cached Gservices settings. + */ + private class GservicesChangedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + // Log.i(TAG, "GservicesChangedReceiver.onReceive(): " + intent); + updateGservicesValues(); + } + } + private class LockList extends ArrayList<WakeLock> { void addLock(WakeLock wl) diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java index f4b3f87..f56088c 100644 --- a/services/java/com/android/server/SensorService.java +++ b/services/java/com/android/server/SensorService.java @@ -16,24 +16,20 @@ package com.android.server; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; import android.hardware.ISensorService; -import android.hardware.SensorManager; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.IBinder; -import android.os.SystemProperties; import android.util.Config; import android.util.Log; -import com.android.internal.R; import java.util.ArrayList; +import com.android.internal.app.IBatteryStats; +import com.android.server.am.BatteryStatsService; + /** * Class that manages the device's sensors. It register clients and activate @@ -43,46 +39,50 @@ import java.util.ArrayList; */ class SensorService extends ISensorService.Stub { - private static final int SENSOR_NOTIFICATION_ACCURACY_LEVEL = 1; - private static final String TAG = SensorService.class.getSimpleName(); + static final String TAG = SensorService.class.getSimpleName(); private static final boolean DEBUG = false; private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; private static final int SENSOR_DISABLE = -1; - private NotificationManager mNotificationManager; - private int mCompassAccuracy = -1; - private final Context mContext; + + /** + * Battery statistics to be updated when sensors are enabled and diabled. + */ + final IBatteryStats mBatteryStats = BatteryStatsService.getService(); private final class Listener implements IBinder.DeathRecipient { - final IBinder mListener; + final IBinder mToken; int mSensors = 0; int mDelay = 0x7FFFFFFF; - Listener(IBinder listener) { - mListener = listener; + Listener(IBinder token) { + mToken = token; } void addSensor(int sensor, int delay) { - mSensors |= sensor; + mSensors |= (1<<sensor); if (mDelay > delay) mDelay = delay; } void removeSensor(int sensor) { - mSensors &= ~sensor; + mSensors &= ~(1<<sensor); } boolean hasSensor(int sensor) { - return ((mSensors & sensor) != 0); + return ((mSensors & (1<<sensor)) != 0); } public void binderDied() { if (localLOGV) Log.d(TAG, "sensor listener died"); - synchronized(mListeners) { mListeners.remove(this); - for (int sensor = SensorManager.SENSOR_MAX; sensor > 0; sensor >>>= 1) { + mToken.unlinkToDeath(this, 0); + // go through the lists of sensors used by the listener that + // died and deactivate them. + for (int sensor=0 ; sensor<32 && mSensors!=0 ; sensor++) { if (hasSensor(sensor)) { + removeSensor(sensor); try { deactivateIfUnused(sensor); } catch (RemoteException e) { @@ -95,60 +95,31 @@ class SensorService extends ISensorService.Stub { } } + @SuppressWarnings("unused") public SensorService(Context context) { if (localLOGV) Log.d(TAG, "SensorService startup"); _sensors_control_init(); - mNotificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); - mContext = context; } public ParcelFileDescriptor getDataChanel() throws RemoteException { return _sensors_control_open(); } - public void reportAccuracy(int sensor, int value) { - if ((sensor & (SensorManager.SENSOR_ORIENTATION|SensorManager.SENSOR_ORIENTATION_RAW)) != 0) { - synchronized (mNotificationManager) { - if (value != mCompassAccuracy) { - Log.d(TAG, "Compass needs calibration, accuracy=" + value); - if (!SystemProperties.getBoolean("debug.sensors.notification", false)) { - // don't show the sensors notification by default - return; - } - mCompassAccuracy = value; - if (value == -1) { - mNotificationManager.cancel(0); - } else { - if (value <= SENSOR_NOTIFICATION_ACCURACY_LEVEL) { - long token = Binder.clearCallingIdentity(); - try { - CharSequence banner = mContext.getString(R.string.compass_accuracy_banner); - CharSequence title = mContext.getString(R.string.compass_accuracy_notificaction_title); - CharSequence body = mContext.getString(R.string.compass_accuracy_notificaction_body); - Notification n = new Notification(R.drawable.stat_notify_calibrate_compass, - banner, System.currentTimeMillis()); - Intent bogusIntent = new Intent(mContext, SensorService.class); - PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, bogusIntent, - PendingIntent.FLAG_CANCEL_CURRENT|PendingIntent.FLAG_ONE_SHOT); - n.setLatestEventInfo(mContext, title, body, contentIntent); - mNotificationManager.notify(0, n); - } finally { - Binder.restoreCallingIdentity(token); - } - } else { - mNotificationManager.cancel(0); - } - } - } - } - } - } - - public boolean enableSensor(IBinder listener, int sensor, int enable) + public boolean enableSensor(IBinder binder, int sensor, int enable) throws RemoteException { if (localLOGV) Log.d(TAG, "enableSensor " + sensor + " " + enable); + + // Inform battery statistics service of status change + int uid = Binder.getCallingUid(); + long identity = Binder.clearCallingIdentity(); + if (enable == SENSOR_DISABLE) { + mBatteryStats.noteStopSensor(uid, sensor); + } else { + mBatteryStats.noteStartSensor(uid, sensor); + } + Binder.restoreCallingIdentity(identity); - if (listener == null) throw new NullPointerException("listener is null in enableSensor"); + if (binder == null) throw new NullPointerException("listener is null in enableSensor"); synchronized(mListeners) { if (enable!=SENSOR_DISABLE && !_sensors_control_activate(sensor, true)) { @@ -156,22 +127,18 @@ class SensorService extends ISensorService.Stub { return false; } - IBinder binder = listener; Listener l = null; - int minDelay = enable; - int size = mListeners.size(); - for (int i = 0; i < size ; i++) { - Listener test = mListeners.get(i); - if (binder.equals(test.mListener)) { - l = test; + for (Listener listener : mListeners) { + if (binder == listener.mToken) { + l = listener; } - if (minDelay > test.mDelay) - minDelay = test.mDelay; + if (minDelay > listener.mDelay) + minDelay = listener.mDelay; } if (l == null && enable!=SENSOR_DISABLE) { - l = new Listener(listener); + l = new Listener(binder); binder.linkToDeath(l, 0); mListeners.add(l); mListeners.notify(); @@ -182,7 +149,7 @@ class SensorService extends ISensorService.Stub { } if (minDelay >= 0) { - _sensors_control_set_delay(minDelay); + _sensors_control_set_delay(minDelay); } if (enable != SENSOR_DISABLE) { @@ -196,24 +163,28 @@ class SensorService extends ISensorService.Stub { mListeners.notify(); } } + + if (mListeners.size() == 0) { + _sensors_control_wake(); + } } return true; } - private void deactivateIfUnused(int sensor) throws RemoteException { + void deactivateIfUnused(int sensor) throws RemoteException { int size = mListeners.size(); - for (int i = 0; i < size ; i++) { + for (int i=0 ; i<size ; i++) { if (mListeners.get(i).hasSensor(sensor)) return; } _sensors_control_activate(sensor, false); - reportAccuracy(sensor, -1); } - private ArrayList<Listener> mListeners = new ArrayList<Listener>(); + ArrayList<Listener> mListeners = new ArrayList<Listener>(); private static native int _sensors_control_init(); private static native ParcelFileDescriptor _sensors_control_open(); private static native boolean _sensors_control_activate(int sensor, boolean activate); private static native int _sensors_control_set_delay(int ms); + private static native int _sensors_control_wake(); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index ff74472..baf57bc 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -33,11 +33,13 @@ import android.os.IBinder; import android.provider.Settings; import android.provider.Contacts.People; import android.server.BluetoothDeviceService; +import android.server.BluetoothA2dpService; import android.server.checkin.FallbackCheckinService; import android.server.search.SearchManagerService; import android.util.EventLog; import android.util.Log; +import dalvik.system.TouchDex; import dalvik.system.VMRuntime; import com.android.server.am.ActivityManagerService; @@ -46,7 +48,6 @@ import com.android.server.status.StatusBarService; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; - class ServerThread extends Thread { private static final String TAG = "SystemServer"; private final static boolean INCLUDE_DEMO = false; @@ -61,9 +62,9 @@ class ServerThread extends Thread { } @Override public void onChange(boolean selfChange) { - boolean enableAdb = (Settings.System.getInt(mContentResolver, - Settings.System.ADB_ENABLED, 0) > 0); - // setting this system property will start or stop adbd + boolean enableAdb = (Settings.Secure.getInt(mContentResolver, + Settings.Secure.ADB_ENABLED, 0) > 0); + // setting this secure property will start or stop adbd SystemProperties.set("persist.service.adb.enable", enableAdb ? "1" : "0"); } } @@ -83,12 +84,13 @@ class ServerThread extends Thread { String factoryTestStr = SystemProperties.get("ro.factorytest"); int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF : Integer.parseInt(factoryTestStr); - + PowerManagerService power = null; IPackageManager pm = null; Context context = null; WindowManagerService wm = null; BluetoothDeviceService bluetooth = null; + BluetoothA2dpService bluetoothA2dp = null; HeadsetObserver headset = null; // Critical services... @@ -96,7 +98,7 @@ class ServerThread extends Thread { Log.i(TAG, "Starting Power Manager."); power = new PowerManagerService(); ServiceManager.addService(Context.POWER_SERVICE, power); - + Log.i(TAG, "Starting Activity Manager."); context = ActivityManagerService.main(factoryTest); @@ -104,43 +106,44 @@ class ServerThread extends Thread { ServiceManager.addService("telephony.registry", new TelephonyRegistry(context)); AttributeCache.init(context); - + Log.i(TAG, "Starting Package Manager."); pm = PackageManagerService.main(context, factoryTest != SystemServer.FACTORY_TEST_OFF); ActivityManagerService.setSystemProcess(); - + mContentResolver = context.getContentResolver(); - + Log.i(TAG, "Starting Content Manager."); ContentService.main(context, factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL); - + Log.i(TAG, "Starting System Content Providers."); ActivityManagerService.installSystemProviders(); - + Log.i(TAG, "Starting Battery Service."); BatteryService battery = new BatteryService(context); ServiceManager.addService("battery", battery); - // only initialize the power service after we have started the + // only initialize the power service after we have started the // content providers and the batter service. power.init(context, ActivityManagerService.getDefault(), battery); - + Log.i(TAG, "Starting Alarm Manager."); AlarmManagerService alarm = new AlarmManagerService(context); ServiceManager.addService(Context.ALARM_SERVICE, alarm); Watchdog.getInstance().init(context, battery, power, alarm, ActivityManagerService.self()); - + // Sensor Service is needed by Window Manager, so this goes first Log.i(TAG, "Starting Sensor Service."); ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context)); Log.i(TAG, "Starting Window Manager."); - wm = WindowManagerService.main(context, power); + wm = WindowManagerService.main(context, power, + factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ((ActivityManagerService)ServiceManager.getService("activity")) @@ -160,9 +163,12 @@ class ServerThread extends Thread { bluetooth = new BluetoothDeviceService(context); bluetooth.init(); ServiceManager.addService(Context.BLUETOOTH_SERVICE, bluetooth); + bluetoothA2dp = new BluetoothA2dpService(context); + ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE, + bluetoothA2dp); - int bluetoothOn = Settings.System.getInt(mContentResolver, - Settings.System.BLUETOOTH_ON, 0); + int bluetoothOn = Settings.Secure.getInt(mContentResolver, + Settings.Secure.BLUETOOTH_ON, 0); if (bluetoothOn > 0) { bluetooth.enable(null); } @@ -172,8 +178,10 @@ class ServerThread extends Thread { Log.e("System", "Failure starting core service", e); } + StatusBarService statusBar = null; + InputMethodManagerService imm = null; + if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { - StatusBarService statusBar = null; try { Log.i(TAG, "Starting Status Bar Service."); statusBar = new StatusBarService(context); @@ -182,7 +190,22 @@ class ServerThread extends Thread { } catch (Throwable e) { Log.e(TAG, "Failure starting StatusBarService", e); } - + + try { + Log.i(TAG, "Starting Clipboard Service."); + ServiceManager.addService("clipboard", new ClipboardService(context)); + } catch (Throwable e) { + Log.e(TAG, "Failure starting Clipboard Service", e); + } + + try { + Log.i(TAG, "Starting Input Method Service."); + imm = new InputMethodManagerService(context, statusBar); + ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm); + } catch (Throwable e) { + Log.e(TAG, "Failure starting Input Manager Service", e); + } + try { Log.i(TAG, "Starting Hardware Service."); ServiceManager.addService("hardware", new HardwareService(context)); @@ -199,7 +222,7 @@ class ServerThread extends Thread { try { Log.i(TAG, "Starting Connectivity Service."); - ServiceManager.addService(Context.CONNECTIVITY_SERVICE, + ServiceManager.addService(Context.CONNECTIVITY_SERVICE, ConnectivityService.getInstance(context)); } catch (Throwable e) { Log.e(TAG, "Failure starting Connectivity Service", e); @@ -217,25 +240,25 @@ class ServerThread extends Thread { // MountService must start after NotificationManagerService Log.i(TAG, "Starting Mount Service."); ServiceManager.addService("mount", new MountService(context)); - } catch (Throwable e) { Log.e(TAG, "Failure starting Mount Service", e); } try { - Log.i(TAG, "Starting DeviceMemoryMonitor service"); - ServiceManager.addService("devicememorymonitor", new DeviceMemoryMonitorService(context)); + Log.i(TAG, "Starting DeviceStorageMonitor service"); + ServiceManager.addService(DeviceStorageMonitorService.SERVICE, + new DeviceStorageMonitorService(context)); } catch (Throwable e) { - Log.e(TAG, "Failure starting DeviceMemoryMonitor service", e); + Log.e(TAG, "Failure starting DeviceStorageMonitor service", e); } - + try { Log.i(TAG, "Starting Location Manager."); ServiceManager.addService(Context.LOCATION_SERVICE, new LocationManagerService(context)); } catch (Throwable e) { Log.e(TAG, "Failure starting Location Manager", e); } - + try { Log.i(TAG, "Starting Search Service."); ServiceManager.addService( Context.SEARCH_SERVICE, new SearchManagerService(context) ); @@ -243,18 +266,11 @@ class ServerThread extends Thread { Log.e(TAG, "Failure starting Search Service", e); } - try { - Log.i(TAG, "Starting Clipboard Service."); - ServiceManager.addService("clipboard", new ClipboardService(context)); - } catch (Throwable e) { - Log.e(TAG, "Failure starting Clipboard Service", e); - } - if (INCLUDE_DEMO) { Log.i(TAG, "Installing demo data..."); (new DemoThread(context)).start(); } - + try { Log.i(TAG, "Starting Checkin Service"); addService(context, "checkin", "com.google.android.server.checkin.CheckinService", @@ -276,7 +292,7 @@ class ServerThread extends Thread { } catch (Throwable e) { Log.e(TAG, "Failure starting Volume Service", e); } - + try { Log.i(TAG, "Starting HeadsetObserver"); // Listen for wired headset changes @@ -286,30 +302,36 @@ class ServerThread extends Thread { } } - // make sure the ADB_ENABLED setting value matches the system property value - Settings.System.putInt(mContentResolver, Settings.System.ADB_ENABLED, + // make sure the ADB_ENABLED setting value matches the secure property value + Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED, "1".equals(SystemProperties.get("persist.service.adb.enable")) ? 1 : 0); // register observer to listen for settings changes - mContentResolver.registerContentObserver(Settings.System.getUriFor(Settings.System.ADB_ENABLED), + mContentResolver.registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED), false, new AdbSettingsObserver()); // It is now time to start up the app processes... + if (statusBar != null) { + statusBar.systemReady(); + } + if (imm != null) { + imm.systemReady(); + } wm.systemReady(); power.systemReady(); try { pm.systemReady(); } catch (RemoteException e) { } - + // After making the following code, third party code may be running... try { ActivityManagerNative.getDefault().systemReady(); } catch (RemoteException e) { } - + Watchdog.getInstance().start(); - + Looper.loop(); Log.d(TAG, "System ServerThread is exiting!"); } diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index 70ade1d..dbaf086 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -16,19 +16,18 @@ package com.android.server; -import android.app.ActivityManagerNative; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.os.Bundle; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.telephony.CellLocation; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.TelephonyManager; -import android.util.Log; +import android.text.TextUtils; import java.util.ArrayList; import java.io.FileDescriptor; @@ -41,9 +40,6 @@ import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneStateIntentReceiver; import com.android.internal.telephony.TelephonyIntents; -import static android.Manifest.permission.READ_PHONE_STATE; -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; - /** * Since phone process can be restarted, this class provides a centralized @@ -408,21 +404,23 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { Bundle data = new Bundle(); state.fillInNotifierBundle(data); intent.putExtras(data); - broadcastStickyIntent(intent); + mContext.sendStickyBroadcast(intent); } private void broadcastSignalStrengthChanged(int asu) { Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED); intent.putExtra(PhoneStateIntentReceiver.INTENT_KEY_ASU, asu); - broadcastStickyIntent(intent); + mContext.sendStickyBroadcast(intent); } private void broadcastCallStateChanged(int state, String incomingNumber) { - Intent intent = new Intent(TelephonyIntents.ACTION_PHONE_STATE_CHANGED); + Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED); intent.putExtra(Phone.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString()); - intent.putExtra(PhoneStateIntentReceiver.INTENT_KEY_NUM, incomingNumber); - broadcastStickyIntent(intent); + if (!TextUtils.isEmpty(incomingNumber)) { + intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber); + } + mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE); } private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible, @@ -437,16 +435,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } intent.putExtra(Phone.DATA_APN_KEY, apn); intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName); - broadcastStickyIntent(intent); + mContext.sendStickyBroadcast(intent); } private void broadcastDataConnectionFailed(String reason) { Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); intent.putExtra(Phone.FAILURE_REASON_KEY, reason); - broadcastStickyIntent(intent); - } - - private static void broadcastStickyIntent(Intent intent) { - ActivityManagerNative.broadcastStickyIntent(intent, null); + mContext.sendStickyBroadcast(intent); } } diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index 70f54ca..70c3110 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -783,7 +783,15 @@ public class Watchdog extends Thread { c.set(Calendar.SECOND, (int)secondsSinceMidnight - (val*60)); c.set(Calendar.MILLISECOND, 0); - return c.getTimeInMillis(); + long newTime = c.getTimeInMillis(); + if (newTime < curTime) { + // The given time (in seconds since midnight) has already passed for today, so advance + // by one day (due to daylight savings, etc., the delta may differ from 24 hours). + c.add(Calendar.DAY_OF_MONTH, 1); + newTime = c.getTimeInMillis(); + } + + return newTime; } @Override diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 8f69235..7a0deff 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -47,8 +47,8 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; +import android.os.BatteryManager; import android.provider.Settings; -import android.provider.Settings.Gservices; import android.util.Log; import android.text.TextUtils; @@ -72,7 +72,7 @@ import java.io.PrintWriter; 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{1,}"); + private static final Pattern scanResultPattern = Pattern.compile("\t+"); private final WifiStateTracker mWifiStateTracker; private Context mContext; @@ -81,15 +81,16 @@ public class WifiService extends IWifiManager.Stub { private AlarmManager mAlarmManager; private PendingIntent mIdleIntent; private static final int IDLE_REQUEST = 0; + private boolean mScreenOff; private boolean mDeviceIdle; - private boolean mChargerAttached; - private final LockList mLocks = new LockList(); + private int mPluggedType; + private final LockList mLocks = new LockList(); /** - * See {@link Gservices#WIFI_IDLE_MS}. This is the default value if a - * Gservices value is not present. + * See {@link Settings.System#WIFI_IDLE_MS}. This is the default value if a + * Settings.System value is not present. */ - private long mIdleMillis = 2 * 60 * 1000; /* 2 minutes */ + private static final long DEFAULT_IDLE_MILLIS = 2 * 60 * 1000; /* 2 minutes */ private static final String WAKELOCK_TAG = "WifiService"; @@ -99,10 +100,11 @@ public class WifiService extends IWifiManager.Stub { * observed to take about 5 seconds under normal circumstances. This * provides a bit of extra margin. * <p> - * See {@link Gservices#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}. - * This is the default value if a Gservices value is not present. + * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}. + * This is the default value if a Settings.System value is not present. */ - private int mWakelockTimeout = 8000; + private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000; + // Wake lock used by driver-stop operation private static PowerManager.WakeLock mDriverStopWakeLock; // Wake lock used by other operations @@ -151,6 +153,12 @@ public class WifiService extends IWifiManager.Stub { private char[] mScanResultBuffer; private boolean mNeedReconfig; + /** + * Number of allowed radio frequency channels in various regulatory domains. + * This list is sufficient for 802.11b/g networks (2.4GHz range). + */ + private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14}; + private static final String ACTION_DEVICE_IDLE = "com.android.server.WifiManager.action.DEVICE_IDLE"; @@ -164,11 +172,6 @@ public class WifiService extends IWifiManager.Stub { mIsHiddenNetworkPresent = new HashMap<Integer, Boolean>(); mNumHiddenNetworkPresent = 0; - ContentResolver contentResolver = context.getContentResolver(); - mIdleMillis = Gservices.getLong(contentResolver, Gservices.WIFI_IDLE_MS, mIdleMillis); - mWakelockTimeout = Gservices.getInt(contentResolver, - Gservices.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS, mWakelockTimeout); - mScanResultCache = new LinkedHashMap<String, ScanResult>( SCAN_RESULT_CACHE_SIZE, 0.75f, true) { /* @@ -214,7 +217,7 @@ public class WifiService extends IWifiManager.Stub { Log.d(TAG, "WifiService starting up with Wi-Fi " + (wifiEnabled ? "enabled" : "disabled")); registerForBroadcasts(); - setWifiEnabledBlocking(wifiEnabled); + setWifiEnabledBlocking(wifiEnabled, false); } /** @@ -369,16 +372,16 @@ public class WifiService extends IWifiManager.Stub { private boolean getPersistedWifiEnabled() { final ContentResolver cr = mContext.getContentResolver(); try { - return Settings.System.getInt(cr, Settings.System.WIFI_ON) == 1; + return Settings.Secure.getInt(cr, Settings.Secure.WIFI_ON) == 1; } catch (Settings.SettingNotFoundException e) { - Settings.System.putInt(cr, Settings.System.WIFI_ON, 0); + Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, 0); return false; } } private void persistWifiEnabled(boolean enabled) { final ContentResolver cr = mContext.getContentResolver(); - Settings.System.putInt(cr, Settings.System.WIFI_ON, enabled ? 1 : 0); + Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0); } NetworkStateTracker getNetworkStateTracker() { @@ -433,9 +436,7 @@ public class WifiService extends IWifiManager.Stub { */ synchronized (mWifiHandler) { mWakeLock.acquire(); - - mWifiHandler.obtainMessage( - enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI).sendToTarget(); + sendEnableMessage(enable, true); } return true; @@ -444,10 +445,11 @@ public class WifiService extends IWifiManager.Stub { /** * 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. * @return {@code true} if the operation succeeds (or if the existing state * is the same as the requested state) */ - private boolean setWifiEnabledBlocking(boolean enable) { + private boolean setWifiEnabledBlocking(boolean enable, boolean persist) { final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED; if (mWifiState == eventualWifiState) { @@ -501,13 +503,17 @@ public class WifiService extends IWifiManager.Stub { // Success! - persistWifiEnabled(enable); + if (persist) { + persistWifiEnabled(enable); + } updateWifiState(eventualWifiState); /* - * Initiliaze the hidden networs state if the Wi-Fi is being turned on. + * Initialize the hidden networks state and the number of allowed + * radio channels if Wi-Fi is being turned on. */ if (enable) { + mWifiStateTracker.setNumAllowedChannels(); initializeHiddenNetworksState(); } @@ -799,7 +805,7 @@ public class WifiService extends IWifiManager.Stub { */ int netId = config.networkId; boolean newNetwork = netId == -1; - boolean doReconfig = false; + boolean doReconfig; int currentPriority; // networkId of -1 means we want to create a new network if (newNetwork) { @@ -1350,6 +1356,78 @@ public class WifiService extends IWifiManager.Stub { } /** + * 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 is + * persisted as a System setting. + * @param numChannels the number of allowed channels. Must be greater than 0 + * and less than or equal to 16. + * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., + * {@code numChannels} is outside the valid range. + */ + public boolean setNumAllowedChannels(int numChannels) { + 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. + */ + boolean found = false; + for (int validChan : sValidRegulatoryChannelCounts) { + if (validChan == numChannels) { + found = true; + break; + } + } + if (!found) { + return false; + } + + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, + numChannels); + mWifiStateTracker.setNumAllowedChannels(numChannels); + return true; + } + + /** + * 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; + + enforceAccessPermission(); + synchronized (mWifiStateTracker) { + /* + * 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 = WifiNative.getNumAllowedChannelsCommand(); + if (numChannels < 0) { + numChannels = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, + -1); + } + } + return numChannels; + } + + /** + * 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; + } + + /** * Return the DHCP-assigned addresses from the last successful DHCP request, * if any. * @return the DHCP information @@ -1364,29 +1442,83 @@ public class WifiService extends IWifiManager.Stub { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + long idleMillis = Settings.System.getLong(mContext.getContentResolver(), + Settings.System.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); + int stayAwakeConditions = + Settings.System.getInt(mContext.getContentResolver(), + Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { /* do nothing, we'll check isAirplaneModeOn later. */ } else if (action.equals(Intent.ACTION_SCREEN_ON)) { mAlarmManager.cancel(mIdleIntent); mDeviceIdle = false; + mScreenOff = false; } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { - long triggerTime = System.currentTimeMillis() + mIdleMillis; - mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); + mScreenOff = true; + /* + * 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 + * current power conditions (i.e, not plugged in, plugged in to USB, + * or plugged in to AC). + */ + if (!shouldStayAwake(stayAwakeConditions, mPluggedType)) { + long triggerTime = System.currentTimeMillis() + idleMillis; + 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)) { mDeviceIdle = true; } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { - int plugged = intent.getIntExtra("plugged", 0); - mChargerAttached = (plugged != 0); + /* + * Set a timer to put Wi-Fi to sleep, but only if the screen is off + * AND we are transitioning from a state in which the device was supposed + * to stay awake and a state in which it is not supposed to stay awake. + * If "stay awake" state is not changing, we do nothing, to avoid resetting + * the already-set timer. + */ + int pluggedType = intent.getIntExtra("plugged", 0); + if (mScreenOff && shouldStayAwake(stayAwakeConditions, mPluggedType) && + !shouldStayAwake(stayAwakeConditions, pluggedType)) { + long triggerTime = System.currentTimeMillis() + idleMillis; + mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent); + mPluggedType = pluggedType; + return; + } + mPluggedType = pluggedType; } else { return; } updateWifiState(); } + + /** + * Determine whether the bit value corresponding to {@code pluggedType} is set in + * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value + * 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 — 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 + * being made + * @return {@code true} if {@code pluggedType} indicates that the device is + * supposed to stay awake, {@code false} otherwise. + */ + private boolean shouldStayAwake(int stayAwakeConditions, int pluggedType) { + return (stayAwakeConditions & pluggedType) != 0; + } }; + private void sendEnableMessage(boolean enable, boolean persist) { + Message msg = Message.obtain(mWifiHandler, + (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI), + (persist ? 1 : 0), 0); + msg.sendToTarget(); + } + private void updateWifiState() { boolean wifiEnabled = getPersistedWifiEnabled(); boolean airplaneMode = isAirplaneModeOn(); @@ -1399,17 +1531,22 @@ public class WifiService extends IWifiManager.Stub { if (wifiShouldBeEnabled) { if (wifiShouldBeStarted) { mWakeLock.acquire(); - mWifiHandler.obtainMessage(MESSAGE_ENABLE_WIFI).sendToTarget(); + sendEnableMessage(true, false); mWakeLock.acquire(); - mWifiHandler.obtainMessage(MESSAGE_START_WIFI).sendToTarget(); + mWifiHandler.sendEmptyMessage(MESSAGE_START_WIFI); } else { + int wakeLockTimeout = + Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS, + DEFAULT_WAKELOCK_TIMEOUT); mDriverStopWakeLock.acquire(); - mWifiHandler.obtainMessage(MESSAGE_STOP_WIFI).sendToTarget(); - mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, mWakelockTimeout); + mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI); + mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout); } } else { mWakeLock.acquire(); - mWifiHandler.obtainMessage(MESSAGE_DISABLE_WIFI).sendToTarget(); + sendEnableMessage(false, false); } } } @@ -1454,24 +1591,24 @@ public class WifiService extends IWifiManager.Stub { switch (msg.what) { case MESSAGE_ENABLE_WIFI: { - setWifiEnabledBlocking(true); + setWifiEnabledBlocking(true, msg.arg1 == 1); /* fall through */ } case MESSAGE_START_WIFI: { - WifiNative.startDriverCommand(); + mWifiStateTracker.startDriver(); mWakeLock.release(); break; } case MESSAGE_DISABLE_WIFI: { - setWifiEnabledBlocking(false); + setWifiEnabledBlocking(false, msg.arg1 == 1); mWakeLock.release(); break; } case MESSAGE_STOP_WIFI: { - WifiNative.stopDriverCommand(); + mWifiStateTracker.stopDriver(); // don't release wakelock break; } @@ -1487,7 +1624,7 @@ public class WifiService extends IWifiManager.Stub { } } } - + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) @@ -1498,6 +1635,9 @@ public class WifiService extends IWifiManager.Stub { return; } pw.println("Wi-Fi is " + stateName(mWifiState)); + pw.println("stay-awake conditions: " + + Settings.System.getInt(mContext.getContentResolver(), + Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0)); pw.println(); pw.println("Latest scan results:"); diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index f9edab4..e686bf0 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -31,7 +31,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemProperties; -import android.provider.Settings.System; +import android.provider.Settings; import android.text.TextUtils; import android.util.Config; import android.util.Log; @@ -139,7 +139,7 @@ public class WifiWatchdogService { private void registerForSettingsChanges() { ContentResolver contentResolver = mContext.getContentResolver(); contentResolver.registerContentObserver( - System.getUriFor(System.WIFI_WATCHDOG_ON), false, + Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON), false, new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { @@ -156,84 +156,90 @@ public class WifiWatchdogService { } /** - * @see System#WIFI_WATCHDOG_ON + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON */ private boolean isWatchdogEnabled() { - return System.getInt(mContentResolver, System.WIFI_WATCHDOG_ON, 1) == 1; + return Settings.Secure.getInt(mContentResolver, Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1; } /** - * @see System#WIFI_WATCHDOG_AP_COUNT + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_AP_COUNT */ private int getApCount() { - return System.getInt(mContentResolver, System.WIFI_WATCHDOG_AP_COUNT, 2); + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_AP_COUNT, 2); } /** - * @see System#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT */ private int getInitialIgnoredPingCount() { - return System.getInt(mContentResolver, System.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT , 2); + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT , 2); } /** - * @see System#WIFI_WATCHDOG_PING_COUNT + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_COUNT */ private int getPingCount() { - return System.getInt(mContentResolver, System.WIFI_WATCHDOG_PING_COUNT, 4); + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_PING_COUNT, 4); } /** - * @see System#WIFI_WATCHDOG_PING_TIMEOUT_MS + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_TIMEOUT_MS */ private int getPingTimeoutMs() { - return System.getInt(mContentResolver, System.WIFI_WATCHDOG_PING_TIMEOUT_MS, 500); + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS, 500); } /** - * @see System#WIFI_WATCHDOG_PING_DELAY_MS + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_PING_DELAY_MS */ private int getPingDelayMs() { - return System.getInt(mContentResolver, System.WIFI_WATCHDOG_PING_DELAY_MS, 250); + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS, 250); } /** - * @see System#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE */ private int getAcceptablePacketLossPercentage() { - return System.getInt(mContentResolver, - System.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE, 25); + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE, 25); } /** - * @see System#WIFI_WATCHDOG_MAX_AP_CHECKS + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS */ private int getMaxApChecks() { - return System.getInt(mContentResolver, System.WIFI_WATCHDOG_MAX_AP_CHECKS, 7); + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_MAX_AP_CHECKS, 7); } /** - * @see System#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED */ private boolean isBackgroundCheckEnabled() { - return System.getInt(mContentResolver, System.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED, 1) - == 1; + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED, 1) == 1; } /** - * @see System#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS */ private int getBackgroundCheckDelayMs() { - return System.getInt(mContentResolver, - System.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS, 5000); + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS, 5000); } /** - * @see System#WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS */ private int getBackgroundCheckTimeoutMs() { - return System.getInt(mContentResolver, - System.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS, 1000); + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS, 1000); } /** @@ -326,7 +332,7 @@ public class WifiWatchdogService { int pingDelay = getPingDelayMs(); int acceptableLoss = getAcceptablePacketLossPercentage(); - /** See {@link System#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} */ + /** See {@link Secure#WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT} */ int ignoredPingCounter = 0; int pingCounter = 0; int successCounter = 0; @@ -959,7 +965,7 @@ public class WifiWatchdogService { * thread will ensure when it sees the message that the state is still * valid for going to sleep. * <p> - * For an explanation of sleep, see {@link System#WIFI_WATCHDOG_MAX_AP_CHECKS}. + * For an explanation of sleep, see {@link android.provider.Settings.Secure#WIFI_WATCHDOG_MAX_AP_CHECKS}. */ static final int MESSAGE_SLEEP = 101; /** Disables the watchdog. */ @@ -1166,7 +1172,7 @@ public class WifiWatchdogService { private static class DnsPinger { /** Number of bytes for the query */ - private static final int DNS_QUERY_BASE_SIZE = 12; + private static final int DNS_QUERY_BASE_SIZE = 33; /** The DNS port */ private static final int DNS_PORT = 53; @@ -1227,21 +1233,54 @@ public class WifiWatchdogService { } private static void fillQuery(byte[] buf) { - int i = 0; - // See RFC2929, section 2 + /* + * See RFC2929 (though the bit tables in there are misleading for + * us. For example, the recursion desired bit is the 0th bit for us, + * but looking there it would appear as the 7th bit of the byte + */ + + // Make sure it's all zeroed out + for (int i = 0; i < buf.length; i++) buf[i] = 0; + + // Form a query for www.android.com - // [0-1] bytes are an ID - buf[i++] = (byte) sRandom.nextInt(256); - buf[i++] = (byte) sRandom.nextInt(256); + // [0-1] bytes are an ID, generate random ID for this query + buf[0] = (byte) sRandom.nextInt(256); + buf[1] = (byte) sRandom.nextInt(256); // [2-3] bytes are for flags. - // The Opcode is '2' for server status. It is on bits [1-4] of this byte. - buf[i++] = 4; - buf[i++] = 0; + buf[2] = 1; // Recursion desired + + // [4-5] bytes are for the query count + buf[5] = 1; // One query + + // [6-7] [8-9] [10-11] are all counts of other fields we don't use + + // [12-15] for www + writeString(buf, 12, "www"); - // [4-5] [6-7] [8-9] [10-11] are all counts of other fields we don't use - for (; i <= 11; i++) buf[i] = 0; + // [16-23] for android + writeString(buf, 16, "android"); + + // [24-27] for com + writeString(buf, 24, "com"); + + // [29-30] bytes are for QTYPE, set to 1 + buf[30] = 1; + + // [31-32] bytes are for QCLASS, set to 1 + buf[32] = 1; + } + + private static void writeString(byte[] buf, int startPos, String string) { + int pos = startPos; + + // Write the length first + buf[pos++] = (byte) string.length(); + for (int i = 0; i < string.length(); i++) { + buf[pos++] = (byte) string.charAt(i); + } } } } diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 4ad2c42..9671743 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -25,6 +25,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND; 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_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.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_GPU; @@ -32,11 +34,16 @@ import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_HARDWARE; 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; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import com.android.internal.app.IBatteryStats; import com.android.internal.policy.PolicyManager; +import com.android.internal.view.IInputContext; +import com.android.internal.view.IInputMethodClient; +import com.android.internal.view.IInputMethodManager; import com.android.server.KeyInputQueue.QueuedEvent; -import com.android.server.am.BatteryStats; +import com.android.server.am.BatteryStatsService; import android.Manifest; import android.app.ActivityManagerNative; @@ -49,6 +56,7 @@ import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; +import android.os.BatteryStats; import android.os.Binder; import android.os.Debug; import android.os.Handler; @@ -84,6 +92,7 @@ import android.view.RawInputEvent; import android.view.Surface; import android.view.SurfaceSession; import android.view.View; +import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; @@ -152,6 +161,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo */ static final int MAX_ANIMATION_DURATION = 10*1000; + /** Amount of time (in milliseconds) to animate the dim surface from one + * value to another, when no window animation is driving it. + */ + static final int DEFAULT_DIM_DURATION = 200; + + static final int UPDATE_FOCUS_NORMAL = 0; + static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; + static final int UPDATE_FOCUS_PLACING_SURFACES = 2; + static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3; + private static final String SYSTEM_SECURE = "ro.secure"; /** @@ -177,6 +196,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final Context mContext; + final boolean mHaveInputMethods; + final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager(); final IActivityManager mActivityManager; @@ -207,6 +228,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>(); /** + * Window tokens that are in the process of exiting, but still + * on screen for animations. + */ + final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>(); + + /** * Z-ordered (bottom-most first) list of all application tokens, for * controlling the ordering of windows in different applications. This * contains WindowToken objects. @@ -259,9 +286,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo */ ArrayList<WindowState> mForceRemoves; + IInputMethodManager mInputMethodManager; + SurfaceSession mFxSession; Surface mDimSurface; boolean mDimShown; + float mDimCurrentAlpha; + float mDimTargetAlpha; + float mDimDeltaPerMs; + long mLastDimAnimTime; Surface mBlurSurface; boolean mBlurShown; @@ -306,13 +339,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo WindowState mCurrentFocus = null; WindowState mLastFocus = null; + + // 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 mInputMethodWindow = null; + final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>(); AppWindowToken mFocusedApp = null; PowerManagerService mPowerManager; float mWindowAnimationScale = 1.0f; - float mTransitionAnimationScale = 0.0f; + float mTransitionAnimationScale = 1.0f; final KeyWaiter mKeyWaiter = new KeyWaiter(); final KeyQ mQueue; @@ -329,9 +369,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private ViewServer mViewServer; - public static WindowManagerService main( - Context context, PowerManagerService pm) { - WMThread thr = new WMThread(context, pm); + final Rect mTempRect = new Rect(); + + public static WindowManagerService main(Context context, + PowerManagerService pm, boolean haveInputMethods) { + WMThread thr = new WMThread(context, pm, haveInputMethods); thr.start(); synchronized (thr) { @@ -351,16 +393,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private final Context mContext; private final PowerManagerService mPM; + private final boolean mHaveInputMethods; - public WMThread(Context context, PowerManagerService pm) { + public WMThread(Context context, PowerManagerService pm, + boolean haveInputMethods) { super("WindowManager"); mContext = context; mPM = pm; + mHaveInputMethods = haveInputMethods; } public void run() { Looper.prepare(); - WindowManagerService s = new WindowManagerService(mContext, mPM); + WindowManagerService s = new WindowManagerService(mContext, mPM, + mHaveInputMethods); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_DISPLAY); @@ -407,14 +453,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } - private WindowManagerService(Context context, PowerManagerService pm) { + private WindowManagerService(Context context, PowerManagerService pm, + boolean haveInputMethods) { mContext = context; + mHaveInputMethods = haveInputMethods; mPowerManager = pm; mPowerManager.setPolicy(mPolicy); mActivityManager = ActivityManagerNative.getDefault(); - mBatteryStats = BatteryStats.getService(); + mBatteryStats = BatteryStatsService.getService(); // Get persisted window scale setting mWindowAnimationScale = Settings.System.getFloat(context.getContentResolver(), @@ -450,7 +498,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo try { return super.onTransact(code, data, reply, flags); } catch (RuntimeException e) { - // The activity manager only throws security exceptions, so let's + // The window manager only throws security exceptions, so let's // log all others. if (!(e instanceof SecurityException)) { Log.e(TAG, "Window Manager Crash", e); @@ -674,9 +722,144 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } + int findDesiredInputMethodWindowIndexLocked() { + final ArrayList localmWindows = mWindows; + final int N = localmWindows.size(); + WindowState w = null; + int i = N; + while (i > 0) { + i--; + w = (WindowState)localmWindows.get(i); + final int fl = w.mAttrs.flags + & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM); + //Log.i(TAG, "Checking window @" + i + " " + w + " fl=0x" + // + Integer.toHexString(fl)); + if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) { + //Log.i(TAG, "Putting input method here!"); + if (w.isVisibleOrAdding()) { + break; + } + } + } + //Log.i(TAG, "Placing input method @" + (i+1)); + if (w != null) { + mInputMethodTarget = w; + return i+1; + } + mInputMethodTarget = null; + return -1; + } + + void addInputMethodWindowToListLocked(WindowState win) { + int pos = findDesiredInputMethodWindowIndexLocked(); + if (pos >= 0) { + win.mTargetAppToken = mInputMethodTarget.mAppToken; + mWindows.add(pos, win); + moveInputMethodDialogsLocked(pos+1); + return; + } + win.mTargetAppToken = null; + addWindowToListInOrderLocked(win); + moveInputMethodDialogsLocked(pos); + } + + void moveInputMethodDialogsLocked(int pos) { + ArrayList<WindowState> dialogs = mInputMethodDialogs; + final int N = dialogs.size(); + for (int i=0; i<N; i++) { + int wpos = mWindows.indexOf(dialogs.get(i)); + if (wpos >= 0) { + if (wpos < pos) pos--; + mWindows.remove(wpos); + } + } + if (pos >= 0) { + final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; + WindowState wp = (WindowState)mWindows.get(pos); + if (wp == mInputMethodWindow) { + pos++; + } + for (int i=0; i<N; i++) { + WindowState win = dialogs.get(i); + win.mTargetAppToken = targetAppToken; + mWindows.add(pos, win); + pos++; + } + return; + } + for (int i=0; i<N; i++) { + WindowState win = dialogs.get(i); + win.mTargetAppToken = null; + addWindowToListInOrderLocked(win); + } + } + + boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) { + final WindowState imWin = mInputMethodWindow; + final int DN = mInputMethodDialogs.size(); + if (imWin == null && DN == 0) { + return false; + } + + int imPos = findDesiredInputMethodWindowIndexLocked(); + if (imPos >= 0) { + // In this case, the input method windows are to be placed + // immediately above the window they are targeting. + + WindowState firstImWin = imPos < DN + ? (WindowState)mWindows.get(imPos) : null; + if (imWin != null) { + if (imWin == firstImWin) { + // Already at the correct location! + return false; + } + } else { + if (mInputMethodDialogs.get(0) == firstImWin) { + // Already at the correct location! + return false; + } + } + + if (imWin != null) { + int oldPos = mWindows.indexOf(imWin); + mWindows.remove(oldPos); + if (imPos > oldPos) imPos--; + imWin.mTargetAppToken = mInputMethodTarget.mAppToken; + mWindows.add(imPos, imWin); + if (DN > 0) moveInputMethodDialogsLocked(imPos+1); + } else { + moveInputMethodDialogsLocked(imPos); + } + + } else { + // In this case, the input method windows go in a fixed layer, + // because they aren't currently associated with a focus window. + + if (imWin != null) { + mWindows.remove(imWin); + imWin.mTargetAppToken = null; + addWindowToListInOrderLocked(imWin); + if (DN > 0) moveInputMethodDialogsLocked(-1);; + } else { + moveInputMethodDialogsLocked(-1);; + } + + } + + if (needAssignLayers) { + assignLayersLocked(); + } + + return true; + } + + void adjustInputMethodDialogsLocked() { + moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked()); + } + public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, - Rect outCoveredInsets) { + Rect outContentInsets) { int res = mPolicy.checkAddPermission(attrs); if (res != WindowManagerImpl.ADD_OKAY) { return res; @@ -703,7 +886,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } if (attrs.type >= FIRST_SUB_WINDOW && attrs.type <= LAST_SUB_WINDOW) { - attachedWindow = windowForClientLocked(session, attrs.token); + attachedWindow = windowForClientLocked(null, attrs.token); if (attachedWindow == null) { Log.w(TAG, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting."); @@ -726,7 +909,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + attrs.token + ". Aborting."); return WindowManagerImpl.ADD_BAD_APP_TOKEN; } - token = new WindowToken(attrs.token); + if (attrs.type == TYPE_INPUT_METHOD) { + Log.w(TAG, "Attempted to add input method window with unknown token " + + attrs.token + ". Aborting."); + return WindowManagerImpl.ADD_BAD_APP_TOKEN; + } + token = new WindowToken(attrs.token, -1); addToken = true; } else if (attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type <= LAST_APPLICATION_WINDOW) { @@ -746,6 +934,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo TAG, "**** NO NEED TO START: " + attrs.getTitle()); return WindowManagerImpl.ADD_STARTING_NOT_NEEDED; } + } else if (attrs.type == TYPE_INPUT_METHOD) { + if (token.windowType != TYPE_INPUT_METHOD) { + Log.w(TAG, "Attempted to add input method window with bad token " + + attrs.token + ". Aborting."); + return WindowManagerImpl.ADD_BAD_APP_TOKEN; + } } win = new WindowState(session, client, token, @@ -783,19 +977,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo token.appWindowToken.startingWindow = win; } - addWindowToListInOrderLocked(win); + boolean imMayMove = true; - assignLayersLocked(); - // Don't do layout here, the window must call - // relayout to be displayed, so we'll do it there. + if (attrs.type == TYPE_INPUT_METHOD) { + mInputMethodWindow = win; + addInputMethodWindowToListLocked(win); + imMayMove = false; + } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) { + mInputMethodDialogs.add(win); + adjustInputMethodDialogsLocked(); + imMayMove = false; + } else { + addWindowToListInOrderLocked(win); + } - //dump(); - Binder.restoreCallingIdentity(origId); win.mEnterAnimationPending = true; - mPolicy.getCoveredInsetHintLw(attrs, outCoveredInsets); + mPolicy.getContentInsetHintLw(attrs, outContentInsets); if (mInTouchMode) { res |= WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE; @@ -805,9 +1005,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } if (win.canReceiveKeys()) { - updateFocusedWindowLocked(); + if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS)) { + imMayMove = false; + } + } + + if (imMayMove) { + moveInputMethodWindowsIfNeededLocked(false); } + assignLayersLocked(); + // Don't do layout here, the window must call + // relayout to be displayed, so we'll do it there. + + //dump(); + if (localLOGV) Log.v( TAG, "New client " + client.asBinder() + ": window=" + win); @@ -859,7 +1071,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.mSurface != null && !mDisplayFrozen) { // If we are not currently running the exit animation, we // need to see about starting one. - if (!win.mExiting && !win.mDrawPending && !win.mCommitDrawPending) { + if (win.isVisible()) { int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; @@ -875,8 +1087,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo win.mExiting = true; win.mRemoveOnExit = true; mLayoutNeeded = true; + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); performLayoutAndPlaceSurfacesLocked(); - updateFocusedWindowLocked(); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); } @@ -887,7 +1099,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } removeWindowInnerLocked(session, win); - updateFocusedWindowLocked(); + updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); Binder.restoreCallingIdentity(origId); } @@ -901,6 +1113,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mWindowMap.remove(win.mClient.asBinder()); mWindows.remove(win); + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { + mInputMethodDialogs.remove(win); + } + final WindowToken token = win.mToken; final AppWindowToken atoken = win.mAppToken; token.windows.remove(win); @@ -967,10 +1185,44 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } + void setInsetsWindow(Session session, IWindow client, + int touchableInsets, Rect contentInsets, + Rect visibleInsets) { + long origId = Binder.clearCallingIdentity(); + try { + synchronized (mWindowMap) { + WindowState w = windowForClientLocked(session, client); + if (w != null) { + w.mGivenInsetsPending = false; + w.mGivenContentInsets.set(contentInsets); + w.mGivenVisibleInsets.set(visibleInsets); + w.mTouchableInsets = touchableInsets; + mLayoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + public void getWindowDisplayFrame(Session session, IWindow client, + Rect outDisplayFrame) { + synchronized(mWindowMap) { + WindowState win = windowForClientLocked(session, client); + if (win == null) { + outDisplayFrame.setEmpty(); + return; + } + outDisplayFrame.set(win.mDisplayFrame); + } + } - public int relayoutWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, - int requestedWidth, int requestedHeight, int viewVisibility, Rect outFrame, - Rect outCoveredInsets, Surface outSurface) { + public int relayoutWindow(Session session, IWindow client, + WindowManager.LayoutParams attrs, int requestedWidth, + int requestedHeight, int viewVisibility, boolean insetsPending, + Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, + Surface outSurface) { boolean displayed = false; boolean inTouchMode; @@ -988,12 +1240,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mPolicy.adjustWindowParamsLw(attrs); } - boolean windowfocusabilityChanged = attrs != null && - ((attrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != - (win.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)); - int attrChanges = 0; - if (attrs != null) attrChanges = win.mAttrs.copyFrom(attrs); + int flagChanges = 0; + if (attrs != null) { + flagChanges = win.mAttrs.flags ^= attrs.flags; + attrChanges = win.mAttrs.copyFrom(attrs); + } if (localLOGV) Log.v( TAG, "Relayout given client " + client.asBinder() @@ -1016,10 +1268,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo (attrs.height / (float)requestedHeight) : 1.0f; } + boolean imMayMove = (flagChanges&( + WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0; + boolean focusMayChange = win.mViewVisibility != viewVisibility - || windowfocusabilityChanged + || ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) || (!win.mRelayoutCalled); + win.mRelayoutCalled = true; + final int oldVisibility = win.mViewVisibility; win.mViewVisibility = viewVisibility; if (viewVisibility == View.VISIBLE && (win.mAppToken == null || !win.mAppToken.clientHidden)) { @@ -1032,6 +1290,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo win.mDestroying = false; mDestroySurface.remove(win); } + if (oldVisibility == View.GONE) { + win.mEnterAnimationPending = true; + } if (displayed && win.mSurface != null && !win.mDrawPending && !win.mCommitDrawPending && !mDisplayFrozen) { applyEnterAnimationLocked(win); @@ -1053,7 +1314,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (displayed) { focusMayChange = true; } + if (win.mAttrs.type == TYPE_INPUT_METHOD + && mInputMethodWindow == null) { + mInputMethodWindow = win; + imMayMove = true; + } } else { + win.mEnterAnimationPending = false; if (win.mSurface != null) { // If we are not currently running the exit animation, we // need to see about starting one. @@ -1064,7 +1331,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } - if (!win.mExiting && !win.mDrawPending && + if (win.isVisible() && applyAnimationLocked(win, transit, false)) { win.mExiting = true; mKeyWaiter.finishedKey(session, client, true, @@ -1078,16 +1345,41 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } } + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } outSurface.clear(); } + boolean assignLayers = false; + + if (focusMayChange) { + //System.out.println("Focus may change: " + win.mAttrs.getTitle()); + if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) { + assignLayers = true; + imMayMove = false; + } + //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus); + } + + if (imMayMove) { + if (moveInputMethodWindowsIfNeededLocked(false)) { + assignLayers = true; + } + } + mLayoutNeeded = true; + win.mGivenInsetsPending = insetsPending; + if (assignLayers) { + assignLayersLocked(); + } performLayoutAndPlaceSurfacesLocked(); if (win.mAppToken != null) { win.mAppToken.updateReportedVisibilityLocked(); } outFrame.set(win.mFrame); - outCoveredInsets.set(win.mCoveredInsets); + outContentInsets.set(win.mContentInsets); + outVisibleInsets.set(win.mVisibleInsets); if (localLOGV) Log.v( TAG, "Relayout given client " + client.asBinder() + ", requestedWidth=" + requestedWidth @@ -1099,12 +1391,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (localLOGV || DEBUG_FOCUS) Log.v( TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange); - if (focusMayChange) { - //System.out.println("Focus may change: " + win.mAttrs.getTitle()); - updateFocusedWindowLocked(); - //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus); - } - inTouchMode = mInTouchMode; } @@ -1348,6 +1634,76 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return wtoken.appWindowToken; } + public void addWindowToken(IBinder token, int type) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "addWindowToken()")) { + return; + } + + synchronized(mWindowMap) { + WindowToken wtoken = mTokenMap.get(token); + if (wtoken != null) { + Log.w(TAG, "Attempted to add existing input method token: " + token); + return; + } + wtoken = new WindowToken(token, type); + mTokenMap.put(token, wtoken); + mTokenList.add(wtoken); + } + } + + public void removeWindowToken(IBinder token) { + if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, + "removeWindowToken()")) { + return; + } + + final long origId = Binder.clearCallingIdentity(); + synchronized(mWindowMap) { + WindowToken wtoken = mTokenMap.remove(token); + mTokenList.remove(wtoken); + if (wtoken != null) { + boolean delayed = false; + if (!wtoken.hidden) { + wtoken.hidden = true; + + final int N = wtoken.windows.size(); + boolean changed = false; + + for (int i=0; i<N; i++) { + WindowState win = wtoken.windows.get(i); + + if (win.isAnimating()) { + delayed = true; + } + + if (win.isVisibleNow()) { + applyAnimationLocked(win, + WindowManagerPolicy.TRANSIT_EXIT, false); + mKeyWaiter.finishedKey(win.mSession, win.mClient, true, + KeyWaiter.RETURN_NOTHING); + changed = true; + } + } + + if (changed) { + mLayoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); + } + + if (delayed) { + mExitingTokens.add(wtoken); + } + } + + } else { + Log.w(TAG, "Attempted to remove non-existing token: " + token); + } + } + Binder.restoreCallingIdentity(origId); + } + public void addAppToken(int addPos, IApplicationToken token, int groupId, int requestedOrientation, boolean fullscreen) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, @@ -1523,7 +1879,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (moveFocusNow && changed) { final long origId = Binder.clearCallingIdentity(); - updateFocusedWindowLocked(); + updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); Binder.restoreCallingIdentity(origId); } } @@ -1665,10 +2021,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo ttoken.updateLayers(); } + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); assignLayersLocked(); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); - updateFocusedWindowLocked(); Binder.restoreCallingIdentity(origId); return; } else if (ttoken.startingData != null) { @@ -1743,11 +2099,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden + " performLayout=" + performLayout); + boolean runningAppAnimation = false; + if (transit != WindowManagerPolicy.TRANSIT_NONE) { + if (wtoken.animation == sDummyAnimation) { + wtoken.animation = null; + } applyAnimationLocked(wtoken, lp, transit, visible); changed = true; if (wtoken.animation != null) { - delayed = true; + delayed = runningAppAnimation = true; } } @@ -1764,10 +2125,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo //Log.i(TAG, "Window " + win + ": vis=" + win.isVisible()); //win.dump(" "); if (visible) { - if (!win.isVisible()) { + if (!win.isVisibleNow()) { + if (!runningAppAnimation) { + applyAnimationLocked(win, + WindowManagerPolicy.TRANSIT_ENTER, true); + } changed = true; } - } else if (win.isVisible()) { + } else if (win.isVisibleNow()) { + if (!runningAppAnimation) { + applyAnimationLocked(win, + WindowManagerPolicy.TRANSIT_EXIT, false); + } mKeyWaiter.finishedKey(win.mSession, win.mClient, true, KeyWaiter.RETURN_NOTHING); changed = true; @@ -1793,8 +2162,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (changed && performLayout) { mLayoutNeeded = true; + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); + assignLayersLocked(); performLayoutAndPlaceSurfacesLocked(); - updateFocusedWindowLocked(); } } @@ -2007,10 +2377,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " animation=" + wtoken.animation + " animating=" + wtoken.animating); if (delayed) { + // set the token aside because it has an active animation to be finished mExitingAppTokens.add(wtoken); - } else { - mAppTokens.remove(wtoken); } + mAppTokens.remove(wtoken); wtoken.removed = true; if (wtoken.startingData != null) { startingToken = wtoken; @@ -2019,7 +2389,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (mFocusedApp == wtoken) { if (DEBUG_FOCUS) Log.v(TAG, "Removing focused app token:" + wtoken); mFocusedApp = null; - updateFocusedWindowLocked(); + updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); mKeyWaiter.tickle(); } } else { @@ -2169,10 +2539,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken); if (DEBUG_REORDER) Log.v(TAG, "Final window list:"); if (DEBUG_REORDER) dumpWindowsLocked(); + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); assignLayersLocked(); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); - updateFocusedWindowLocked(); } Binder.restoreCallingIdentity(origId); } @@ -2217,10 +2587,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } + updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES); assignLayersLocked(); mLayoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); - updateFocusedWindowLocked(); //dump(); } @@ -2888,6 +3258,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } config.orientation = orientation; config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO; + config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; mPolicy.adjustConfigurationLw(config); Log.i(TAG, "Input configuration changed: " + config); long now = SystemClock.uptimeMillis(); @@ -3074,6 +3445,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo KeyWaiter.RETURN_PENDING_POINTER, qev); ev = null; } else { + if (action == MotionEvent.ACTION_DOWN) { + WindowState out = mKeyWaiter.mOutsideTouchTargets; + if (out != null) { + MotionEvent oev = MotionEvent.obtain(ev); + oev.setAction(MotionEvent.ACTION_OUTSIDE); + do { + final Rect frame = out.mFrame; + oev.offsetLocation(-(float)frame.left, -(float)frame.top); + try { + out.mClient.dispatchPointer(oev, eventTime); + } catch (android.os.RemoteException e) { + Log.i(TAG, "WINDOW DIED during outside motion dispatch: " + out); + } + oev.offsetLocation((float)frame.left, (float)frame.top); + out = out.mNextOutsideTouch; + } while (out != null); + mKeyWaiter.mOutsideTouchTargets = null; + } + } final Rect frame = target.mFrame; ev.offsetLocation(-(float)frame.left, -(float)frame.top); mKeyWaiter.bindTargetWindowLocked(target); @@ -3303,7 +3693,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo /** * Inject a pointer (touch) event into the UI. * - * @param ev A motion event describing the trackball action. (As noted in + * @param ev A motion event describing the pointer (touch) action. (As noted in * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use * {@link SystemClock#uptimeMillis()} as the timebase.) * @param sync If true, wait for the event to be completed before returning to the caller. @@ -3368,6 +3758,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Target of Motion events WindowState mMotionTarget; + + // Windows above the target who would like to receive an "outside" + // touch event for any down events outside of them. + WindowState mOutsideTouchTargets; /** * Wait for the last event dispatch to complete, then find the next @@ -3563,6 +3957,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev, MotionEvent nextMotion, boolean isPointerEvent) { + mOutsideTouchTargets = null; + if (nextKey != null) { // Find the target window for a normal key event. final int keycode = nextKey.getKeyCode(); @@ -3654,8 +4050,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final ArrayList windows = mWindows; final int N = windows.size(); WindowState topErrWindow = null; + final Rect tmpRect = mTempRect; for (int i=N-1; i>=0; i--) { WindowState child = (WindowState)windows.get(i); + //Log.i(TAG, "Checking dispatch to: " + child); final int flags = child.mAttrs.flags; if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) { if (topErrWindow == null) { @@ -3663,24 +4061,59 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } if (!child.isVisible()) { + //Log.i(TAG, "Not visible!"); continue; } - final Rect frame = child.mFrame; if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { + //Log.i(TAG, "Not touchable!"); + if ((flags & WindowManager.LayoutParams + .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { + child.mNextOutsideTouch = mOutsideTouchTargets; + mOutsideTouchTargets = child; + } continue; } + tmpRect.set(child.mFrame); + if (child.mTouchableInsets == ViewTreeObserver + .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) { + // The touch is inside of the window if it is + // inside the frame, AND the content part of that + // frame that was given by the application. + tmpRect.left += child.mGivenContentInsets.left; + tmpRect.top += child.mGivenContentInsets.top; + tmpRect.right -= child.mGivenContentInsets.right; + tmpRect.bottom -= child.mGivenContentInsets.bottom; + } else if (child.mTouchableInsets == ViewTreeObserver + .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) { + // The touch is inside of the window if it is + // inside the frame, AND the visible part of that + // frame that was given by the application. + tmpRect.left += child.mGivenVisibleInsets.left; + tmpRect.top += child.mGivenVisibleInsets.top; + tmpRect.right -= child.mGivenVisibleInsets.right; + tmpRect.bottom -= child.mGivenVisibleInsets.bottom; + } final int touchFlags = flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); - if (frame.contains(x, y) || (touchFlags == 0)) { + if (tmpRect.contains(x, y) || touchFlags == 0) { + //Log.i(TAG, "Using this target!"); if (!screenWasOff || (flags & WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) { mMotionTarget = child; } else { + //Log.i(TAG, "Waking, skip!"); mMotionTarget = null; } break; } + + if ((flags & WindowManager.LayoutParams + .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { + child.mNextOutsideTouch = mOutsideTouchTargets; + mOutsideTouchTargets = child; + //Log.i(TAG, "Adding to outside target list: " + child); + } } // if there's an error window but it's not accepting @@ -3816,10 +4249,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo MotionEvent res = (MotionEvent)qev.event; if (DEBUG_INPUT) Log.v(TAG, "Returning pending motion: " + res); + mQueue.recycleEvent(qev); if (win != null && returnWhat == RETURN_PENDING_POINTER) { res.offsetLocation(-win.mFrame.left, -win.mFrame.top); } - mQueue.recycleEvent(qev); return res; } return null; @@ -4261,7 +4694,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private final class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { - final IBinder mToken; + final IInputMethodClient mClient; + final IInputContext mInputContext; final int mUid; final int mPid; SurfaceSession mSurfaceSession; @@ -4282,14 +4716,39 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo QueuedEvent mPendingTrackballMove; WindowState mPendingTrackballWindow; - public Session(IBinder token) { - mToken = token; + public Session(IInputMethodClient client, IInputContext inputContext) { + mClient = client; + mInputContext = inputContext; mUid = Binder.getCallingUid(); mPid = Binder.getCallingPid(); + synchronized (mWindowMap) { + if (mInputMethodManager == null && mHaveInputMethods) { + IBinder b = ServiceManager.getService( + Context.INPUT_METHOD_SERVICE); + mInputMethodManager = IInputMethodManager.Stub.asInterface(b); + } + } + long ident = Binder.clearCallingIdentity(); try { - token.linkToDeath(this, 0); + // Note: it is safe to call in to the input method manager + // here because we are not holding our lock. + if (mInputMethodManager != null) { + mInputMethodManager.addClient(client, inputContext, + mUid, mPid); + } else { + client.setUsingInputMethod(false); + } + client.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { // The caller has died, so we can just forget about this. + try { + if (mInputMethodManager != null) { + mInputMethodManager.removeClient(client); + } + } catch (RemoteException ee) { + } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -4308,6 +4767,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } public void binderDied() { + // Note: it is safe to call in to the input method manager + // here because we are not holding our lock. + try { + if (mInputMethodManager != null) { + mInputMethodManager.removeClient(mClient); + } + } catch (RemoteException e) { + } synchronized(mWindowMap) { mClientDead = true; killSessionLocked(); @@ -4315,8 +4782,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } public int add(IWindow window, WindowManager.LayoutParams attrs, - int viewVisibility, Rect outCoveredInsets) { - return addWindow(this, window, attrs, viewVisibility, outCoveredInsets); + int viewVisibility, Rect outContentInsets) { + return addWindow(this, window, attrs, viewVisibility, outContentInsets); } public void remove(IWindow window) { @@ -4325,10 +4792,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public int relayout(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, - Rect outFrame, Rect outCoveredInsets, Surface outSurface) { + boolean insetsPending, Rect outFrame, Rect outContentInsets, + Rect outVisibleInsets, Surface outSurface) { return relayoutWindow(this, window, attrs, - requestedWidth, requestedHeight, viewFlags, - outFrame, outCoveredInsets, outSurface); + requestedWidth, requestedHeight, viewFlags, insetsPending, + outFrame, outContentInsets, outVisibleInsets, outSurface); + } + + public void setTransparentRegion(IWindow window, Region region) { + setTransparentRegionWindow(this, window, region); + } + + public void setInsets(IWindow window, int touchableInsets, + Rect contentInsets, Rect visibleInsets) { + setInsetsWindow(this, window, touchableInsets, contentInsets, + visibleInsets); + } + + public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { + getWindowDisplayFrame(this, window, outDisplayFrame); } public void finishDrawing(IWindow window) { @@ -4357,10 +4839,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo return mKeyWaiter.finishedKey(this, window, false, KeyWaiter.RETURN_PENDING_TRACKBALL); } - - public void setTransparentRegion(IWindow window, Region region) { - setTransparentRegionWindow(this, window, region); - } public void setInTouchMode(boolean mode) { synchronized(mWindowMap) { @@ -4435,12 +4913,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final IWindow mClient; WindowToken mToken; AppWindowToken mAppToken; + AppWindowToken mTargetAppToken; final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams(); final DeathRecipient mDeathRecipient; final WindowState mAttachedWindow; final ArrayList mChildWindows = new ArrayList(); final int mBaseLayer; final int mSubLayer; + final boolean mLayoutAttached; int mViewVisibility; boolean mPolicyVisibility = true; boolean mAppFreezing; @@ -4458,15 +4938,52 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int mLastLayer; boolean mHaveFrame; + WindowState mNextOutsideTouch; + // Actual frame shown on-screen (may be modified by animation) final Rect mShownFrame = new Rect(); final Rect mLastShownFrame = new Rect(); /** + * Insets that determine the actually visible area + */ + final Rect mVisibleInsets = new Rect(); + final Rect mLastVisibleInsets = new Rect(); + boolean mVisibleInsetsChanged; + + /** * Insets that are covered by system windows */ - final Rect mCoveredInsets = new Rect(); + final Rect mContentInsets = new Rect(); + final Rect mLastContentInsets = new Rect(); + boolean mContentInsetsChanged; + /** + * Set to true if we are waiting for this window to receive its + * given internal insets before laying out other windows based on it. + */ + boolean mGivenInsetsPending; + + /** + * These are the content insets that were given during layout for + * this window, to be applied to windows behind it. + */ + final Rect mGivenContentInsets = new Rect(); + + /** + * These are the visible insets that were given during layout for + * this window, to be applied to windows behind it. + */ + final Rect mGivenVisibleInsets = new Rect(); + + /** + * 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 + * tests. + */ + int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; + // Current transformation being applied. float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; @@ -4479,6 +4996,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final Rect mLastFrame = new Rect(); final Rect mContainingFrame = new Rect(); + final Rect mDisplayFrame = new Rect(); + final Rect mContentFrame = new Rect(); + final Rect mVisibleFrame = new Rect(); float mShownAlpha = 1; float mAlpha = 1; @@ -4550,14 +5070,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } catch (RemoteException e) { mDeathRecipient = null; mAttachedWindow = null; + mLayoutAttached = false; mBaseLayer = 0; mSubLayer = 0; return; } mDeathRecipient = deathRecipient; - if (mAttrs.type >= mAttrs.FIRST_SUB_WINDOW && - mAttrs.type <= mAttrs.LAST_SUB_WINDOW) { + if ((mAttrs.type >= FIRST_SUB_WINDOW && + mAttrs.type <= LAST_SUB_WINDOW)) { // The multiplier here is to reserve space for multiple // windows in the same type layer. mBaseLayer = mPolicy.windowTypeToLayerLw( @@ -4566,6 +5087,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type); mAttachedWindow = attachedWindow; mAttachedWindow.mChildWindows.add(this); + mLayoutAttached = mAttrs.type != + WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; } else { // The multiplier here is to reserve space for multiple // windows in the same type layer. @@ -4574,6 +5097,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + TYPE_LAYER_OFFSET; mSubLayer = 0; mAttachedWindow = null; + mLayoutAttached = false; } WindowState appWin = this; @@ -4609,12 +5133,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mSession.windowAddedLocked(); } - public void computeFrameLw(int pl, int pt, int pr, int pb, - int dl, int dt, int dr, int db) { + public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) { mHaveFrame = true; - final int pw = pr-pl; - final int ph = pb-pt; + final int pw = pf.right-pf.left; + final int ph = pf.bottom-pf.top; int w,h; if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) { @@ -4626,11 +5149,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } final Rect container = mContainingFrame; - container.left = pl; - container.top = pt; - container.right = pr; - container.bottom = pb; + container.set(pf); + + final Rect display = mDisplayFrame; + display.set(df); + final Rect content = mContentFrame; + content.set(cf); + + final Rect visible = mVisibleFrame; + visible.set(vf); + final Rect frame = mFrame; //System.out.println("In: w=" + w + " h=" + h + " container=" + @@ -4643,63 +5172,85 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo //System.out.println("Out: " + mFrame); // Now make sure the window fits in the overall display. - int off = 0; - if (frame.left < dl) off = dl-frame.left; - else if (frame.right > dr) off = dr-frame.right; - if (off != 0) { - if (frame.width() > (dr-dl)) { - frame.left = dl; - frame.right = dr; - } else { - frame.left += off; - frame.right += off; - } - } - off = 0; - if (frame.top < dt) off = dt-frame.top; - else if (frame.bottom > db) off = db-frame.bottom; - if (off != 0) { - if (frame.height() > (db-dt)) { - frame.top = dt; - frame.bottom = db; - } else { - frame.top += off; - frame.bottom += off; - } - } + Gravity.applyDisplay(mAttrs.gravity, df, frame); + + // Make sure the content and visible frames are inside of the + // final window frame. + if (content.left < frame.left) content.left = frame.left; + if (content.top < frame.top) content.top = frame.top; + if (content.right > frame.right) content.right = frame.right; + if (content.bottom > frame.bottom) content.bottom = frame.bottom; + if (visible.left < frame.left) visible.left = frame.left; + if (visible.top < frame.top) visible.top = frame.top; + if (visible.right > frame.right) visible.right = frame.right; + if (visible.bottom > frame.bottom) visible.bottom = frame.bottom; + + final Rect contentInsets = mContentInsets; + contentInsets.left = content.left-frame.left; + contentInsets.top = content.top-frame.top; + contentInsets.right = frame.right-content.right; + contentInsets.bottom = frame.bottom-content.bottom; + + final Rect visibleInsets = mVisibleInsets; + visibleInsets.left = visible.left-frame.left; + visibleInsets.top = visible.top-frame.top; + visibleInsets.right = frame.right-visible.right; + visibleInsets.bottom = frame.bottom-visible.bottom; - if (localLOGV) Log.v(TAG, "Resolving (mRequestedWidth=" - + mRequestedWidth + ", mRequestedheight=" - + mRequestedHeight + ") to" + " (pw=" + pw + ", dh=" + ph - + "): frame=" + mFrame); + if (localLOGV) { + //if ("com.google.android.youtube".equals(mAttrs.packageName) + // && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { + Log.v(TAG, "Resolving (mRequestedWidth=" + + mRequestedWidth + ", mRequestedheight=" + + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph + + "): frame=" + mFrame.toShortString() + + " ci=" + contentInsets.toShortString() + + " vi=" + visibleInsets.toShortString()); + //} + } } - public void setFrameLw(int left, int top, int right, int bottom) - { - mHaveFrame = true; - mFrame.left = left; - mFrame.top = top; - mFrame.right = right; - mFrame.bottom = bottom; + public Rect getFrameLw() { + return mFrame; } - public Rect getFrameLw() - { - return mFrame; + public Rect getDisplayFrameLw() { + return mDisplayFrame; + } + + public Rect getContentFrameLw() { + return mContentFrame; } - public WindowManager.LayoutParams getAttrs() - { + public Rect getVisibleFrameLw() { + return mVisibleFrame; + } + + public boolean getGivenInsetsPendingLw() { + return mGivenInsetsPending; + } + + public Rect getGivenContentInsetsLw() { + return mGivenContentInsets; + } + + public Rect getGivenVisibleInsetsLw() { + return mGivenVisibleInsets; + } + + public WindowManager.LayoutParams getAttrs() { return mAttrs; } - public IApplicationToken getAppToken() - { + public int getSurfaceLayer() { + return mLayer; + } + + public IApplicationToken getAppToken() { return mAppToken != null ? mAppToken.appToken : null; } - public boolean hasAppShownWindows() - { + public boolean hasAppShownWindows() { return mAppToken != null ? mAppToken.firstWindowDrawn : false; } @@ -5125,9 +5676,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // transforming since it is more important to have that // animation be smooth. mShownAlpha = mAlpha; - if (!PixelFormat.formatHasAlpha(mAttrs.format) + if (false && (!PixelFormat.formatHasAlpha(mAttrs.format) || (isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) - && x == frame.left && y == frame.top)) { + && x == frame.left && y == frame.top))) { //Log.i(TAG, "Applying alpha transform"); if (selfTransformation) { mShownAlpha *= mTransformation.getAlpha(); @@ -5154,9 +5705,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mDtDy = 1; } - // Is this window visible? It is not visible if there is no - // surface, or we are in the process of running an exit animation - // that will remove the surface. + /** + * Is this window visible? It is not visible if there is no + * surface, or we are in the process of running an exit animation + * that will remove the surface. + */ boolean isVisible() { final AppWindowToken atoken = mAppToken; return mSurface != null && mPolicyVisibility && !mAttachedHidden @@ -5164,8 +5717,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo && !mExiting && !mDestroying; } - // Same as isVisible(), but we also count it as visible between the - // call to IWindowSession.add() and the first relayout(). + /** + * The same as isVisible(), but follows the current hidden state of + * the associated app token, not the pending requested hidden state. + */ + boolean isVisibleNow() { + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && !mToken.hidden && !mExiting && !mDestroying; + } + + /** + * Same as isVisible(), but we also count it as visible between the + * call to IWindowSession.add() and the first relayout(). + */ boolean isVisibleOrAdding() { final AppWindowToken atoken = mAppToken; return (mSurface != null @@ -5175,9 +5739,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo && !mExiting && !mDestroying; } - // Is this window currently on-screen? It is on-screen either if it - // is visible or it is currently running an animation before no longer - // being visible. + /** + * Is this window currently on-screen? It is on-screen either if it + * is visible or it is currently running an animation before no longer + * being visible. + */ boolean isOnScreen() { final AppWindowToken atoken = mAppToken; if (atoken != null) { @@ -5190,21 +5756,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } - // Like isOnScreen(), but we don't return true if the window is part - // of a transition that has not yet been started. + /** + * Like isOnScreen(), but we don't return true if the window is part + * of a transition that has not yet been started. + */ boolean isReadyForDisplay() { final AppWindowToken atoken = mAppToken; - if (atoken != null) { - return mSurface != null && mPolicyVisibility && !mDestroying - && ((!mAttachedHidden && !atoken.hidden) - || mAnimating || atoken.animating); - } else { - return mSurface != null && mPolicyVisibility && !mDestroying - && (!mAttachedHidden || mAnimating); - } + final boolean animating = atoken != null ? atoken.animating : false; + return mSurface != null && mPolicyVisibility && !mDestroying + && ((!mAttachedHidden && !mToken.hidden) + || mAnimating || animating); } - // Is the window or its container currently animating? + /** Is the window or its container currently animating? */ boolean isAnimating() { final WindowState attached = mAttachedWindow; final AppWindowToken atoken = mAppToken; @@ -5215,13 +5779,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo || atoken.inPendingTransaction)); } - // Is this window currently animating? + /** Is this window currently animating? */ boolean isWindowAnimating() { return mAnimation != null; } - // Like isOnScreen, but returns false if the surface hasn't yet - // been drawn. + /** + * Like isOnScreen, but returns false if the surface hasn't yet + * been drawn. + */ public boolean isDisplayedLw() { final AppWindowToken atoken = mAppToken; return mSurface != null && mPolicyVisibility && !mDestroying @@ -5320,16 +5886,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(prefix + "mSession=" + mSession + " mClient=" + mClient.asBinder()); pw.println(prefix + "mAttrs=" + mAttrs); - pw.println(prefix + "mAttachedWindow=" + mAttachedWindow); + pw.println(prefix + "mAttachedWindow=" + mAttachedWindow + + " mLayoutAttached=" + mLayoutAttached); pw.println(prefix + "mBaseLayer=" + mBaseLayer + " mSubLayer=" + mSubLayer + " mAnimLayer=" + mLayer + "+" - + (mAppToken != null ? mAppToken.animLayerAdjustment : 0) + + (mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment + : (mAppToken != null ? mAppToken.animLayerAdjustment : 0)) + "=" + mAnimLayer + " mLastLayer=" + mLastLayer); pw.println(prefix + "mSurface=" + mSurface); pw.println(prefix + "mToken=" + mToken); pw.println(prefix + "mAppToken=" + mAppToken); + pw.println(prefix + "mTargetAppToken=" + mTargetAppToken); pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility) + " mPolicyVisibility=" + mPolicyVisibility + " mAttachedHidden=" + mAttachedHidden @@ -5337,18 +5906,29 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " mHaveFrame=" + mHaveFrame); pw.println(prefix + "Requested w=" + mRequestedWidth + " h=" + mRequestedHeight + " x=" + mReqXPos + " y=" + mReqYPos); - pw.println(prefix + "mShownFrame=" + mShownFrame - + " last=" + mLastShownFrame); - pw.println(prefix + "mFrame=" + mFrame + " last=" + mLastFrame); - pw.println(prefix + "mContainingFrame=" + mContainingFrame - + " mCoveredInsets=" + mCoveredInsets); + pw.println(prefix + "mGivenContentInsets=" + mGivenContentInsets.toShortString() + + " mGivenVisibleInsets=" + mGivenVisibleInsets.toShortString() + + " mTouchableInsets=" + mTouchableInsets + + " pending=" + mGivenInsetsPending); + pw.println(prefix + "mShownFrame=" + mShownFrame.toShortString() + + " last=" + mLastShownFrame.toShortString()); + pw.println(prefix + "mFrame=" + mFrame.toShortString() + + " last=" + mLastFrame.toShortString()); + pw.println(prefix + "mContainingFrame=" + mContainingFrame.toShortString() + + " mDisplayFrame=" + mDisplayFrame.toShortString()); + pw.println(prefix + "mContentFrame=" + mContentFrame.toShortString() + + " mVisibleFrame=" + mVisibleFrame.toShortString()); + pw.println(prefix + "mContentInsets=" + mContentInsets.toShortString() + + " last=" + mLastContentInsets.toShortString() + + " mVisibleInsets=" + mVisibleInsets.toShortString() + + " last=" + mLastVisibleInsets.toShortString()); pw.println(prefix + "mShownAlpha=" + mShownAlpha + " mAlpha=" + mAlpha + " mLastAlpha=" + mLastAlpha); pw.println(prefix + "mAnimating=" + mAnimating + " mAnimationIsEntrance=" + mAnimationIsEntrance + " mAnimation=" + mAnimation); - pw.println(prefix + "mHasTransformation=" + mHasTransformation - + " mTransformation=" + mTransformation); + pw.println(prefix + "XForm: has=" + mHasTransformation + + " " + mTransformation.toShortString()); pw.println(prefix + "mDrawPending=" + mDrawPending + " mCommitDrawPending=" + mCommitDrawPending + " mReadyToShow=" + mReadyToShow @@ -5366,10 +5946,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + Integer.toHexString(System.identityHashCode(this)) + " " + mAttrs.getTitle() + "}"; } - - public void setCoveredInsetsLw(int l, int t, int r, int b) { - mCoveredInsets.set(l, t, r, b); - } } // ------------------------------------------------------------- @@ -5380,6 +5956,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // The actual token. final IBinder token; + // The type of window this token is for, as per WindowManager.LayoutParams. + final int windowType; + // If this is an AppWindowToken, this is non-null. AppWindowToken appWindowToken; @@ -5389,14 +5968,23 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Is key dispatching paused for this token? boolean paused = false; - WindowToken(IBinder _token) { + // Should this token's windows be hidden? + boolean hidden; + + // Temporary for finding which tokens no longer have visible windows. + boolean hasVisible; + + WindowToken(IBinder _token, int type) { token = _token; + windowType = type; } void dump(PrintWriter pw, String prefix) { pw.println(prefix + this); pw.println(prefix + "token=" + token); pw.println(prefix + "windows=" + windows); + pw.println(prefix + "windowType=" + windowType + " hidden=" + hidden + + " hasVisible=" + hasVisible); } @Override @@ -5432,9 +6020,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // won't be taken into account for setting the screen orientation. boolean willBeHidden; - // Should this token's windows be hidden? - boolean hidden; - // Is this window's surface needed? This is almost like hidden, except // it will sometimes be true a little earlier: when the token has // been shown, but is still waiting for its app transition to execute @@ -5444,9 +6029,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Have we told the window clients to hide themselves? boolean clientHidden; - // Temporary for finding which tokens no longer have visible windows. - boolean hasVisible; - // Last visibility state we reported to the app token. boolean reportedVisible; @@ -5474,7 +6056,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean firstWindowDrawn; AppWindowToken(IApplicationToken _token) { - super(_token.asBinder()); + super(_token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION); appWindowToken = this; appToken = _token; } @@ -5517,9 +6099,22 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo void updateLayers() { final int N = allAppWindows.size(); + final int adj = animLayerAdjustment; for (int i=0; i<N; i++) { WindowState w = allAppWindows.get(i); - w.mAnimLayer = w.mLayer + animLayerAdjustment; + w.mAnimLayer = w.mLayer + adj; + if (w == mInputMethodTarget) { + WindowState imw = mInputMethodWindow; + if (imw != null) { + imw.mAnimLayer = imw.mLayer + adj; + } + int di = mInputMethodDialogs.size(); + while (di > 0) { + di --; + imw = mInputMethodDialogs.get(di); + imw.mAnimLayer = imw.mLayer + adj; + } + } } } @@ -5691,22 +6286,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(prefix + "allAppWindows=" + allAppWindows); pw.println(prefix + "groupId=" + groupId + " requestedOrientation=" + requestedOrientation); - pw.println(prefix + "hidden=" + hidden - + " hiddenRequested=" + hiddenRequested + pw.println(prefix + "hiddenRequested=" + hiddenRequested + " clientHidden=" + clientHidden - + " willBeHidden=" + willBeHidden); + + " willBeHidden=" + willBeHidden + + " reportedVisible=" + reportedVisible); pw.println(prefix + "paused=" + paused + " freezingScreen=" + freezingScreen); pw.println(prefix + "numInterestingWindows=" + numInterestingWindows + " numDrawnWindows=" + numDrawnWindows + " inPendingTransaction=" + inPendingTransaction + " allDrawn=" + allDrawn); - pw.println(prefix + "hasVisible=" + hasVisible - + " reportedVisible=" + reportedVisible); pw.println(prefix + "animating=" + animating + " animation=" + animation); pw.println(prefix + "animLayerAdjustment=" + animLayerAdjustment - + " transformation=" + transformation); + + " transformation=" + transformation.toShortString()); pw.println(prefix + "startingData=" + startingData + " removed=" + removed + " firstWindowDrawn=" + firstWindowDrawn); @@ -6149,10 +6742,29 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // IWindowManager API // ------------------------------------------------------------- - public IWindowSession openSession(IBinder token) { - return new Session(token); + public IWindowSession openSession(IInputMethodClient client, + IInputContext inputContext) { + if (client == null) throw new IllegalArgumentException("null client"); + if (inputContext == null) throw new IllegalArgumentException("null inputContext"); + return new Session(client, inputContext); } + public boolean inputMethodClientHasFocus(IInputMethodClient client) { + synchronized (mWindowMap) { + // The focus for the client is the window immediately below + // where we would place the input method window. + int idx = findDesiredInputMethodWindowIndexLocked(); + if (idx > 0) { + WindowState imFocus = (WindowState)mWindows.get(idx-1); + if (imFocus != null && imFocus.mSession.mClient != null && + imFocus.mSession.mClient.asBinder() == client.asBinder()) { + return true; + } + } + } + return false; + } + // ------------------------------------------------------------- // Internals // ------------------------------------------------------------- @@ -6170,7 +6782,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.w(TAG, "Requested window " + client + " does not exist", ex); return null; } - if (win.mSession != session) { + if (session != null && win.mSession != session) { RuntimeException ex = new RuntimeException(); Log.w(TAG, "Requested window " + client + " is in session " + win.mSession + ", not " + session, ex); @@ -6198,12 +6810,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (w.mBaseLayer == curBaseLayer) { curLayer += WINDOW_LAYER_MULTIPLIER; w.mLayer = curLayer; + } else if (w.mAttrs.type == TYPE_INPUT_METHOD + || w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { + curLayer += WINDOW_LAYER_MULTIPLIER; + w.mLayer = curLayer; } else { curBaseLayer = curLayer = w.mBaseLayer; w.mLayer = curLayer; } - w.mAnimLayer = w.mAppToken != null - ? (w.mLayer + w.mAppToken.animLayerAdjustment) : w.mLayer; + if (w.mTargetAppToken != null) { + w.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment; + } else if (w.mAppToken != null) { + w.mAnimLayer = w.mLayer + w.mAppToken.animLayerAdjustment; + } else { + w.mAnimLayer = w.mLayer; + } //System.out.println( // "Assigned layer " + curLayer + " to " + w.mClient.asBinder()); } @@ -6295,14 +6916,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // windows, since that means "perform layout as normal, // just don't display"). if (!gone || !win.mHaveFrame) { - if (win.mAttachedWindow == null) { + if (!win.mLayoutAttached) { mPolicy.layoutWindowLw(win, win.mAttrs, null); } else { if (topAttached < 0) topAttached = i; } } } - + // Now perform layout of attached windows, which usually // depend on the position of the window they are attached to. // XXX does not deal with windows that are attached to windows @@ -6315,10 +6936,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // 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) { - - mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); + if (win.mLayoutAttached) { + if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled) + || !win.mHaveFrame) { + mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); + } } } @@ -6346,8 +6968,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION"); + // Initialize state of exiting tokens. + for (i=mExitingTokens.size()-1; i>=0; i--) { + mExitingTokens.get(i).hasVisible = false; + } + // Initialize state of exiting applications. - for (i=0; i<mExitingAppTokens.size(); i++) { + for (i=mExitingAppTokens.size()-1; i>=0; i--) { mExitingAppTokens.get(i).hasVisible = false; } @@ -6365,7 +6992,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo do { final int transactionSequence = ++mTransactionSequence; - // Update animations of all applications. + // Update animations of all applications, including those + // associated with exiting/removed apps boolean tokensAnimating = false; final int NAT = mAppTokens.size(); for (i=0; i<NAT; i++) { @@ -6373,6 +7001,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo tokensAnimating = true; } } + final int NEAT = mExitingAppTokens.size(); + for (i=0; i<NEAT; i++) { + if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) { + tokensAnimating = true; + } + } animating = tokensAnimating; restart = false; @@ -6561,7 +7195,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // a new layout to get them all up-to-date. mLayoutNeeded = true; performLayoutLockedInner(); - updateFocusedWindowLocked(); + updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES); restart = true; } @@ -6651,8 +7285,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } if (!w.mAppFreezing) { - if (!w.mLastFrame.equals(w.mFrame)) { + w.mContentInsetsChanged = + !w.mLastContentInsets.equals(w.mContentInsets); + w.mVisibleInsetsChanged = + !w.mLastVisibleInsets.equals(w.mVisibleInsets); + if (!w.mLastFrame.equals(w.mFrame) + || w.mContentInsetsChanged + || w.mVisibleInsetsChanged) { w.mLastFrame.set(w.mFrame); + w.mLastContentInsets.set(w.mContentInsets); + w.mLastVisibleInsets.set(w.mVisibleInsets); // If the orientation is changing, then we need to // hold off on unfreezing the display until this // window has been redrawn; to do that, we need @@ -6784,8 +7426,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo w.mOrientationChanging = false; } } - if (w.mAppToken != null && w.mSurface != null) { - w.mAppToken.hasVisible = true; + if (w.mSurface != null) { + w.mToken.hasVisible = true; } } else { displayed = true; @@ -6809,9 +7451,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo "Orientation change complete in " + w); } } - if (w.mAppToken != null) { - w.mAppToken.hasVisible = true; - } + w.mToken.hasVisible = true; } } else if (w.mOrientationChanging) { if (DEBUG_ORIENTATION) Log.v(TAG, @@ -6842,82 +7482,136 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + ": blurring=" + blurring + " obscured=" + obscured + " displayed=" + displayed); - if (!dimming && (attrFlags&FLAG_DIM_BEHIND) != 0) { - //Log.i(TAG, "DIM BEHIND: " + w); - dimming = true; - mDimShown = true; - if (mDimSurface == null) { + if ((attrFlags&FLAG_DIM_BEHIND) != 0) { + if (!dimming) { + //Log.i(TAG, "DIM BEHIND: " + w); + dimming = true; + mDimShown = true; + if (mDimSurface == null) { + if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + + mDimSurface + ": CREATE"); + try { + mDimSurface = new Surface(mFxSession, 0, + -1, 16, 16, + PixelFormat.OPAQUE, + Surface.FX_SURFACE_DIM); + } catch (Exception e) { + Log.e(TAG, "Exception creating Dim surface", e); + } + } if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " - + mDimSurface + ": CREATE"); - try { - mDimSurface = new Surface(mFxSession, 0, - -1, 16, 16, - PixelFormat.OPAQUE, - Surface.FX_SURFACE_DIM); - } catch (Exception e) { - Log.e(TAG, "Exception creating Dim surface", e); + + mDimSurface + ": SHOW pos=(0,0) (" + + dw + "x" + dh + "), layer=" + (w.mAnimLayer-1)); + if (mDimSurface != null) { + try { + mDimSurface.setPosition(0, 0); + mDimSurface.setSize(dw, dh); + mDimSurface.show(); + } catch (RuntimeException e) { + Log.w(TAG, "Failure showing dim surface", e); + } } } - if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " - + mDimSurface + ": SHOW pos=(0,0) (" + - dw + "x" + dh + "), layer=" + (w.mAnimLayer-1)); - if (mDimSurface != null) { - try { - mDimSurface.setPosition(0, 0); - mDimSurface.setSize(dw, dh); - mDimSurface.setLayer(w.mAnimLayer-1); - mDimSurface.setAlpha(attrs.dimAmount); - mDimSurface.show(); - } catch (RuntimeException e) { - Log.w(TAG, "Failure showing dim surface", e); - } + mDimSurface.setLayer(w.mAnimLayer-1); + final float target = w.mExiting ? 0 : attrs.dimAmount; + if (mDimTargetAlpha != target) { + // If the desired dim level has changed, then + // start an animation to it. + mLastDimAnimTime = currentTime; + long duration = (w.mAnimating && w.mAnimation != null) + ? w.mAnimation.computeDurationHint() + : DEFAULT_DIM_DURATION; + mDimTargetAlpha = target; + mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) + / duration; } } - if (!blurring && (attrFlags&FLAG_BLUR_BEHIND) != 0) { - //Log.i(TAG, "BLUR BEHIND: " + w); - blurring = true; - mBlurShown = true; - if (mBlurSurface == null) { - if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " - + mBlurSurface + ": CREATE"); - try { - mBlurSurface = new Surface(mFxSession, 0, - -1, 16, 16, - PixelFormat.OPAQUE, - Surface.FX_SURFACE_BLUR); - } catch (Exception e) { - Log.e(TAG, "Exception creating Blur surface", e); + if ((attrFlags&FLAG_BLUR_BEHIND) != 0) { + if (!blurring) { + //Log.i(TAG, "BLUR BEHIND: " + w); + blurring = true; + mBlurShown = true; + if (mBlurSurface == null) { + if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + + mBlurSurface + ": CREATE"); + try { + mBlurSurface = new Surface(mFxSession, 0, + -1, 16, 16, + PixelFormat.OPAQUE, + Surface.FX_SURFACE_BLUR); + } catch (Exception e) { + Log.e(TAG, "Exception creating Blur surface", e); + } } - } - if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " - + mBlurSurface + ": SHOW pos=(0,0) (" + - dw + "x" + dh + "), layer=" + (w.mAnimLayer-1)); - if (mBlurSurface != null) { - mBlurSurface.setPosition(0, 0); - mBlurSurface.setSize(dw, dh); - mBlurSurface.setLayer(w.mAnimLayer-2); - try { - mBlurSurface.show(); - } catch (RuntimeException e) { - Log.w(TAG, "Failure showing blur surface", e); + if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + + mBlurSurface + ": SHOW pos=(0,0) (" + + dw + "x" + dh + "), layer=" + (w.mAnimLayer-1)); + if (mBlurSurface != null) { + mBlurSurface.setPosition(0, 0); + mBlurSurface.setSize(dw, dh); + try { + mBlurSurface.show(); + } catch (RuntimeException e) { + Log.w(TAG, "Failure showing blur surface", e); + } } } + mBlurSurface.setLayer(w.mAnimLayer-2); } } } } if (!dimming && mDimShown) { - if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface - + ": HIDE"); - try { - mDimSurface.hide(); - } catch (RuntimeException e) { - Log.w(TAG, "Illegal argument exception hiding dim surface"); + // Time to hide the dim surface... start fading. + if (mDimTargetAlpha != 0) { + mLastDimAnimTime = currentTime; + mDimTargetAlpha = 0; + mDimDeltaPerMs = (-mDimCurrentAlpha) / DEFAULT_DIM_DURATION; } - mDimShown = false; } + if (mDimShown && mLastDimAnimTime != 0) { + mDimCurrentAlpha += mDimDeltaPerMs + * (currentTime-mLastDimAnimTime); + boolean more = true; + if (mDimDeltaPerMs > 0) { + if (mDimCurrentAlpha > mDimTargetAlpha) { + more = false; + } + } else if (mDimDeltaPerMs < 0) { + if (mDimCurrentAlpha < mDimTargetAlpha) { + more = false; + } + } else { + more = false; + } + + // Do we need to continue animating? + if (more) { + if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + + mDimSurface + ": alpha=" + mDimCurrentAlpha); + mDimSurface.setAlpha(mDimCurrentAlpha); + animating = true; + } else { + mDimCurrentAlpha = mDimTargetAlpha; + mLastDimAnimTime = 0; + if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + + mDimSurface + ": final alpha=" + mDimCurrentAlpha); + mDimSurface.setAlpha(mDimCurrentAlpha); + if (!dimming) { + if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface + + ": HIDE"); + try { + mDimSurface.hide(); + } catch (RuntimeException e) { + Log.w(TAG, "Illegal argument exception hiding dim surface"); + } + mDimShown = false; + } + } + } + if (!blurring && mBlurShown) { if (SHOW_TRANSACTIONS) Log.i(TAG, " BLUR " + mBlurSurface + ": HIDE"); @@ -6956,7 +7650,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo WindowState win = mResizingWindows.get(i); try { win.mClient.resized(win.mFrame.width(), - win.mFrame.height(), win.mDrawPending); + win.mFrame.height(), win.mLastContentInsets, + win.mLastVisibleInsets, win.mDrawPending); + win.mContentInsetsChanged = false; + win.mVisibleInsetsChanged = false; } catch (RemoteException e) { win.mOrientationChanging = false; } @@ -6976,14 +7673,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mDestroySurface.clear(); } + // Time to remove any exiting tokens? + for (i=mExitingTokens.size()-1; i>=0; i--) { + WindowToken token = mExitingTokens.get(i); + if (!token.hasVisible) { + mExitingTokens.remove(i); + } + } + // Time to remove any exiting applications? - for (i=0; i<mExitingAppTokens.size(); i++) { + for (i=mExitingAppTokens.size()-1; i>=0; i--) { AppWindowToken token = mExitingAppTokens.get(i); if (!token.hasVisible && !mClosingApps.contains(token)) { mAppTokens.remove(token); mExitingAppTokens.remove(i); - i--; - //dump(); } } @@ -7118,7 +7821,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } - private boolean updateFocusedWindowLocked() { + private boolean updateFocusedWindowLocked(int mode) { WindowState newFocus = computeFocusedWindowLocked(); if (mCurrentFocus != newFocus) { // This check makes sure that we don't already have the focus @@ -7128,11 +7831,26 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } if (localLOGV) Log.v( TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus); + final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; mLosingFocus.remove(newFocus); if (newFocus != null) { mKeyWaiter.handleNewWindowLocked(newFocus); } + + final WindowState imWindow = mInputMethodWindow; + if (newFocus != imWindow && oldFocus != imWindow) { + moveInputMethodWindowsIfNeededLocked( + mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS && + mode != UPDATE_FOCUS_WILL_PLACE_SURFACES); + mLayoutNeeded = true; + if (mode == UPDATE_FOCUS_PLACING_SURFACES) { + performLayoutLockedInner(); + } else if (mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) { + mLayoutNeeded = true; + performLayoutAndPlaceSurfacesLocked(); + } + } return true; } return false; @@ -7286,6 +8004,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(" Window #" + i + ":"); w.dump(pw, " "); } + if (mInputMethodDialogs.size() > 0) { + pw.println(" "); + pw.println(" Input method dialogs:"); + for (int i=mInputMethodDialogs.size()-1; i>=0; i--) { + WindowState w = mInputMethodDialogs.get(i); + pw.println(" IM Dialog #" + i + ": " + w); + } + } if (mPendingRemove.size() > 0) { pw.println(" "); pw.println(" Remove pending for:"); @@ -7295,6 +8021,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo w.dump(pw, " "); } } + if (mForceRemoves != null && mForceRemoves.size() > 0) { + pw.println(" "); + pw.println(" Windows force removing:"); + for (int i=mForceRemoves.size()-1; i>=0; i--) { + WindowState w = mForceRemoves.get(i); + pw.println(" Removing #" + i + ":"); + w.dump(pw, " "); + } + } if (mDestroySurface.size() > 0) { pw.println(" "); pw.println(" Windows waiting to destroy their surface:"); @@ -7333,13 +8068,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo token.dump(pw, " "); } } + if (mTokenList.size() > 0) { + pw.println(" "); + pw.println(" Window token list:"); + for (int i=0; i<mTokenList.size(); i++) { + pw.println(" WindowToken #" + i + ": " + mTokenList.get(i)); + } + } if (mAppTokens.size() > 0) { pw.println(" "); pw.println(" Application tokens in Z order:"); for (int i=mAppTokens.size()-1; i>=0; i--) { - WindowToken token = mAppTokens.get(i); - pw.println(" App Token #" + i + ":"); - token.dump(pw, " "); + pw.println(" AppWindowToken #" + i + ": " + mAppTokens.get(i)); } } if (mFinishedStarting.size() > 0) { @@ -7351,6 +8091,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo token.dump(pw, " "); } } + if (mExitingTokens.size() > 0) { + pw.println(" "); + pw.println(" Exiting tokens:"); + for (int i=mExitingTokens.size()-1; i>=0; i--) { + WindowToken token = mExitingTokens.get(i); + pw.println(" Exiting Token #" + i + ":"); + token.dump(pw, " "); + } + } if (mExitingAppTokens.size() > 0) { pw.println(" "); pw.println(" Exiting application tokens:"); @@ -7364,13 +8113,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(" mCurrentFocus=" + mCurrentFocus); pw.println(" mLastFocus=" + mLastFocus); pw.println(" mFocusedApp=" + mFocusedApp); + pw.println(" mInputMethodTarget=" + mInputMethodTarget); + pw.println(" mInputMethodWindow=" + mInputMethodWindow); pw.println(" mInTouchMode=" + mInTouchMode); pw.println(" mSystemBooted=" + mSystemBooted + " mDisplayEnabled=" + mDisplayEnabled); pw.println(" mLayoutNeeded=" + mLayoutNeeded + " mSurfacesChanged=" + mSurfacesChanged - + " mBlurShown=" + mBlurShown - + " mDimShown=" + mDimShown); + + " mBlurShown=" + mBlurShown); + pw.println(" mDimShown=" + mDimShown + + " current=" + mDimCurrentAlpha + + " target=" + mDimTargetAlpha + + " delta=" + mDimDeltaPerMs + + " lastAnimTime=" + mLastDimAnimTime); pw.println(" mDisplayFrozen=" + mDisplayFrozen + " mWindowsFreezingScreen=" + mWindowsFreezingScreen + " mAppsFreezingScreen=" + mAppsFreezingScreen); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index db1a43a..6c58997f 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -16,6 +16,7 @@ package com.android.server.am; +import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.RuntimeInit; import com.android.server.IntentResolver; import com.android.server.ProcessMap; @@ -37,6 +38,7 @@ import android.app.IIntentReceiver; import android.app.IIntentSender; import android.app.IServiceConnection; import android.app.IThumbnailReceiver; +import android.app.Instrumentation; import android.app.PendingIntent; import android.app.ResultInfo; import android.content.ComponentName; @@ -87,6 +89,8 @@ import android.view.View; import android.view.WindowManager; import android.view.WindowManagerPolicy; +import dalvik.system.Zygote; + import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -619,7 +623,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * All information we have collected about the runtime performance of * any user id that can impact battery performance. */ - final BatteryStats mBatteryStats; + final BatteryStatsService mBatteryStatsService; /** * Current configuration information. HistoryRecord objects are given @@ -1041,7 +1045,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen m.mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch"); m.mLaunchingActivity.setReferenceCounted(false); - m.mBatteryStats.publish(context); + m.mBatteryStatsService.publish(context); synchronized (thr) { thr.mReady = true; @@ -1209,10 +1213,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); systemDir.mkdirs(); - mBatteryStats = new BatteryStats(new File( + mBatteryStatsService = new BatteryStatsService(new File( systemDir, "batterystats.bin").toString()); - mBatteryStats.readLocked(); - mBatteryStats.writeLocked(); + mBatteryStatsService.getActiveStatistics().readLocked(); + mBatteryStatsService.getActiveStatistics().writeLocked(); mConfiguration.makeDefault(); mProcessStats.init(); @@ -1336,19 +1340,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - synchronized(mBatteryStats) { + synchronized(mBatteryStatsService.getActiveStatistics()) { synchronized(mPidsSelfLocked) { if (haveNewCpuStats) { - if (mBatteryStats.isOnBattery()) { + if (mBatteryStatsService.isOnBattery()) { final int N = mProcessStats.countWorkingStats(); for (int i=0; i<N; i++) { ProcessStats.Stats st = mProcessStats.getWorkingStats(i); ProcessRecord pr = mPidsSelfLocked.get(st.pid); if (pr != null) { - BatteryStats.Uid.Proc ps = pr.batteryStats; - ps.userTime += st.rel_utime; - ps.systemTime += st.rel_stime; + BatteryStatsImpl.Uid.Proc ps = pr.batteryStats; + ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); } } } @@ -1357,7 +1360,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mLastWriteTime < (now-BATTERY_STATS_TIME)) { mLastWriteTime = now; - mBatteryStats.writeLocked(); + mBatteryStatsService.getActiveStatistics().writeLocked(); } } } @@ -1627,11 +1630,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app = newProcessRecordLocked(null, info, processName); mProcessNames.put(processName, info.uid, app); } else { - // If this is a new package in the process, then the process does - // not have a unique package name. - if (!info.packageName.equals(app.uniquePackage)) { - app.uniquePackage = null; - } + // If this is a new package in the process, add the package to the list + app.addPackage(info.packageName); } // If the system is not ready yet, then hold off on starting this @@ -1684,13 +1684,23 @@ public final class ActivityManagerService extends ActivityManagerNative implemen uid = 0; } } + int debugFlags = 0; + if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; + } + if ("1".equals(SystemProperties.get("debug.checkjni"))) { + debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; + } + if ("1".equals(SystemProperties.get("debug.assert"))) { + debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; + } int pid = Process.start("android.app.ActivityThread", mSimpleProcessManagement ? app.processName : null, uid, uid, - gids, ((app.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0), null); - BatteryStats bs = app.batteryStats.getBatteryStats(); + gids, debugFlags, null); + BatteryStatsImpl bs = app.batteryStats.getBatteryStats(); synchronized (bs) { if (bs.isOnBattery()) { - app.batteryStats.starts++; + app.batteryStats.incStartsLocked(); } } @@ -2989,8 +2999,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen intent.setComponent(new ComponentName( aInfo.applicationInfo.packageName, aInfo.name)); + // Don't debug things in the system process if (debug) { - setDebugApp(aInfo.processName, true, false); + if (!aInfo.processName.equals("system")) { + setDebugApp(aInfo.processName, true, false); + } } } @@ -3195,7 +3208,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final void stopActivityLocked(HistoryRecord r) { if (DEBUG_SWITCH) Log.d(TAG, "Stopping: " + r); - if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0) { + if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 + || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) { if (!r.finishing) { requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null, "no-history"); @@ -4311,7 +4325,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen thread.asBinder().linkToDeath(new AppDeathRecipient( app, pid, thread), 0); } catch (RemoteException e) { - app.uniquePackage = app.info.packageName; + app.resetPackageList(); startProcessLocked(app, "link fail", processName); return false; } @@ -4353,7 +4367,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // an infinite loop of restarting processes... Log.w(TAG, "Exception thrown during bind!", e); - app.uniquePackage = app.info.packageName; + app.resetPackageList(); startProcessLocked(app, "bind fail", processName); return false; } @@ -4799,6 +4813,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen String msg = "Permission Denial: getIntentSender() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + + ", (need uid=" + uid + ")" + " is not allowed to send as package " + packageName; Log.w(TAG, msg); throw new SecurityException(msg); @@ -4821,7 +4836,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0; final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0; - flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT); + final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0; + flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT + |PendingIntent.FLAG_UPDATE_CURRENT); PendingIntentRecord.Key key = new PendingIntentRecord.Key( type, packageName, activity, resultWho, @@ -4831,6 +4848,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen PendingIntentRecord rec = ref != null ? ref.get() : null; if (rec != null) { if (!cancelCurrent) { + if (updateCurrent) { + rec.key.requestIntent.replaceExtras(intent); + } return rec; } rec.canceled = true; @@ -5721,10 +5741,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen continue; } + final int flags = target.info.flags; + final boolean finishOnTaskLaunch = - (target.info.flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; + (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; final boolean allowTaskReparenting = - (target.info.flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0; + (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0; if (target.task == task) { // We are inside of the task being reset... we'll either @@ -5736,6 +5758,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen taskTopI = targetI; } if (below != null && below.task == task) { + final boolean clearWhenTaskReset = + (target.intent.getFlags() + &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0; if (!finishOnTaskLaunch && target.resultTo != null) { // If this activity is sending a reply to a previous // activity, we can't do anything with it now until @@ -5808,12 +5833,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } replyChainEnd = -1; addRecentTask(target.task); - } else if (forceReset || finishOnTaskLaunch) { + } else if (forceReset || finishOnTaskLaunch + || clearWhenTaskReset) { // If the activity should just be removed -- either // because it asks for it, or the task should be // cleared -- then finish it and anything that is // part of its reply chain. - if (replyChainEnd < 0) { + if (clearWhenTaskReset) { + // In this case, we want to finish this activity + // and everything above it, so be sneaky and pretend + // like these are all in the reply chain. + replyChainEnd = targetI+1; + while (replyChainEnd < mHistory.size() && + ((HistoryRecord)mHistory.get( + replyChainEnd)).task == task) { + replyChainEnd++; + } + replyChainEnd--; + } else if (replyChainEnd < 0) { replyChainEnd = targetI; } HistoryRecord p = null; @@ -6377,9 +6414,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mProvidersByClass.put(cpi.name, cpr); } app.pubProviders.put(cpi.name, cpr); - if (!cpi.applicationInfo.packageName.equals(app.uniquePackage)) { - app.uniquePackage = null; - } + app.addPackage(cpi.applicationInfo.packageName); } } return providers; @@ -6725,9 +6760,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final ProcessRecord newProcessRecordLocked(IApplicationThread thread, ApplicationInfo info, String customProcess) { String proc = customProcess != null ? customProcess : info.processName; - BatteryStats.Uid.Proc ps = null; - synchronized (mBatteryStats) { - ps = mBatteryStats.getProcessStatsLocked(info.uid, proc); + BatteryStatsImpl.Uid.Proc ps = null; + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + ps = stats.getProcessStatsLocked(info.uid, proc); } return new ProcessRecord(ps, thread, info, proc); } @@ -6931,15 +6967,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!(sender instanceof PendingIntentRecord)) { return; } - synchronized(mBatteryStats) { - if (mBatteryStats.isOnBattery()) { - mBatteryStats.enforceCallingPermission(); + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + if (mBatteryStatsService.isOnBattery()) { + mBatteryStatsService.enforceCallingPermission(); PendingIntentRecord rec = (PendingIntentRecord)sender; int MY_UID = Binder.getCallingUid(); int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid; - BatteryStats.Uid.Pkg pkg = mBatteryStats.getPackageStatsLocked(uid, - rec.key.packageName); - pkg.wakeups++; + BatteryStatsImpl.Uid.Pkg pkg = + stats.getPackageStatsLocked(uid, rec.key.packageName); + pkg.incWakeupsLocked(); } } } @@ -7539,6 +7576,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return errList; } + + public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() { + // Lazy instantiation of list + List<ActivityManager.RunningAppProcessInfo> runList = null; + synchronized (this) { + // Iterate across all processes + final int N = mLRUProcesses.size(); + for (int i = 0; i < N; i++) { + ProcessRecord app = mLRUProcesses.get(i); + if ((app.thread != null) && (!app.crashing && !app.notResponding)) { + // Generate process state info for running application + ActivityManager.RunningAppProcessInfo currApp = + new ActivityManager.RunningAppProcessInfo(app.processName, + app.pid, app.getPackageList()); + if (runList == null) { + runList = new ArrayList<ActivityManager.RunningAppProcessInfo>(); + } + runList.add(currApp); + } + } + } + return runList; + } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -8268,7 +8328,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.crashing = false; app.notResponding = false; - app.uniquePackage = app.info.packageName; + app.resetPackageList(); app.thread = null; app.forcingToForeground = null; app.foregroundServices = false; @@ -8532,9 +8592,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r == null) { filter = new Intent.FilterComparison(service.cloneFilter()); ServiceRestarter res = new ServiceRestarter(); - BatteryStats.Uid.Pkg.Serv ss = null; - synchronized (mBatteryStats) { - ss = mBatteryStats.getServiceStatsLocked( + BatteryStatsImpl.Uid.Pkg.Serv ss = null; + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + ss = stats.getServiceStatsLocked( sInfo.applicationInfo.uid, sInfo.packageName, sInfo.name); } @@ -9616,8 +9677,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final int uid = intentExtras != null ? intentExtras.getInt(Intent.EXTRA_UID) : -1; if (uid >= 0) { - synchronized (mBatteryStats) { - mBatteryStats.uidStats.remove(uid); + BatteryStatsImpl bs = mBatteryStatsService.getActiveStatistics(); + synchronized (bs) { + bs.removeUidStatsLocked(uid); } } } else { @@ -9652,11 +9714,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Add to the sticky list if requested. if (sticky) { - if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY) + if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, + callingPid, callingUid) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() + + callingPid + ", uid=" + callingUid + " requires " + android.Manifest.permission.BROADCAST_STICKY; Log.w(TAG, msg); throw new SecurityException(msg); @@ -10433,13 +10495,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (PackageManager.NameNotFoundException e) { } if (ii == null) { - Log.w(TAG, "Unable to find instrumentation info for: " - + className); + reportStartInstrumentationFailure(watcher, className, + "Unable to find instrumentation info for: " + className); return false; } if (ai == null) { - Log.w(TAG, "Unable to find instrumentation target package: " - + ii.targetPackage); + reportStartInstrumentationFailure(watcher, className, + "Unable to find instrumentation target package: " + ii.targetPackage); return false; } @@ -10453,7 +10515,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " not allowed because package " + ii.packageName + " does not have a signature matching the target " + ii.targetPackage; - Log.w(TAG, msg); + reportStartInstrumentationFailure(watcher, className, msg); throw new SecurityException(msg); } @@ -10470,6 +10532,30 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return true; } + + /** + * Report errors that occur while attempting to start Instrumentation. Always writes the + * error to the logs, but if somebody is watching, send the report there too. This enables + * the "am" command to report errors with more information. + * + * @param watcher The IInstrumentationWatcher. Null if there isn't one. + * @param cn The component name of the instrumentation. + * @param report The error report. + */ + private void reportStartInstrumentationFailure(IInstrumentationWatcher watcher, + ComponentName cn, String report) { + Log.w(TAG, report); + try { + if (watcher != null) { + Bundle results = new Bundle(); + results.putString(Instrumentation.REPORT_KEY_IDENTIFIER, "ActivityManagerService"); + results.putString("Error", report); + watcher.instrumentationStatus(cn, -1, results); + } + } catch (RemoteException e) { + Log.w(TAG, e); + } + } void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) { if (app.instrumentationWatcher != null) { @@ -10549,19 +10635,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Configuration newConfig = new Configuration(mConfiguration); changes = newConfig.updateFrom(values); if (changes != 0) { - mConfiguration = newConfig; - if (DEBUG_SWITCH) { Log.i(TAG, "Updating configuration to: " + values); } EventLog.writeEvent(LOG_CONFIGURATION_CHANGED, changes); - - if (values.locale != null && - !mConfiguration.locale.equals(values.locale)) { - saveLocaleLocked(values.locale); + + if (values.locale != null) { + saveLocaleLocked(values.locale, + !values.locale.equals(mConfiguration.locale), + values.userSetLocale); } - + + mConfiguration = newConfig; + Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); msg.obj = new Configuration(mConfiguration); mHandler.sendMessage(msg); @@ -10744,10 +10831,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen /** * Save the locale. You must be inside a synchronized (this) block. */ - private void saveLocaleLocked(Locale l) { - SystemProperties.set("persist.locale.lang", l.getLanguage()); - SystemProperties.set("persist.locale.country", l.getCountry()); - SystemProperties.set("persist.locale.var", l.getVariant()); + private void saveLocaleLocked(Locale l, boolean isDiff, boolean isPersist) { + if(isDiff) { + SystemProperties.set("user.language", l.getLanguage()); + SystemProperties.set("user.region", l.getCountry()); + } + + if(isPersist) { + SystemProperties.set("persist.sys.language", l.getLanguage()); + SystemProperties.set("persist.sys.country", l.getCountry()); + SystemProperties.set("persist.sys.localevar", l.getVariant()); + } } // ========================================================= diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java index 627ff09..3fcfad0 100644 --- a/services/java/com/android/server/am/AppErrorDialog.java +++ b/services/java/com/android/server/am/AppErrorDialog.java @@ -44,7 +44,7 @@ class AppErrorDialog extends BaseErrorDialog { mProc = app; mResult = result; CharSequence name; - if (app.uniquePackage != null && + if ((app.pkgList.size() == 1) && (name=context.getPackageManager().getApplicationLabel(app.info)) != null) { setMessage(res.getString( com.android.internal.R.string.aerr_application, diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java index 353dd7d..7390ed0 100644 --- a/services/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/java/com/android/server/am/AppNotRespondingDialog.java @@ -44,7 +44,7 @@ class AppNotRespondingDialog extends BaseErrorDialog { ? activity.info.loadLabel(context.getPackageManager()) : null; CharSequence name2 = null; - if (app.uniquePackage != null && + if ((app.pkgList.size() == 1) && (name2=context.getPackageManager().getApplicationLabel(app.info)) != null) { if (name1 != null) { resid = com.android.internal.R.string.anr_activity_application; diff --git a/services/java/com/android/server/am/BatteryStats.java b/services/java/com/android/server/am/BatteryStats.java deleted file mode 100644 index ea31dc0..0000000 --- a/services/java/com/android/server/am/BatteryStats.java +++ /dev/null @@ -1,1146 +0,0 @@ -/* - * Copyright (C) 2006 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.am; - -import com.android.internal.app.IBatteryStats; - -import android.content.Context; -import android.os.Binder; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Process; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.util.Config; -import android.util.Log; -import android.util.SparseArray; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.PrintWriter; -import java.util.HashMap; -import java.util.Map; - -/** - * All information we are collecting about things that can happen that impact - * battery life. - */ -public final class BatteryStats extends IBatteryStats.Stub { - public static final int WAKE_TYPE_PARTIAL = 0; - public static final int WAKE_TYPE_FULL = 1; - public static final int WAKE_TYPE_WINDOW = 2; - - /** - * Include all of the loaded data in the stats. - */ - public static final int STATS_LOADED = 0; - - /** - * Include only the last run in the stats. - */ - public static final int STATS_LAST = 1; - - /** - * Include only the current run in the stats. - */ - public static final int STATS_CURRENT = 2; - - static final int VERSION = 11; - - static IBatteryStats sService; - - final File mFile; - final File mBackupFile; - - Context mContext; - - /** - * The statistics we have collected organized by uids. - */ - final SparseArray<Uid> uidStats = new SparseArray<Uid>(); - - int mStartCount; - - long mBatteryUptime; - long mBatteryUptimeStart; - long mBatteryLastUptime; - long mBatteryRealtime; - long mBatteryRealtimeStart; - long mBatteryLastRealtime; - - long mUptime; - long mUptimeStart; - long mLastUptime; - long mRealtime; - long mRealtimeStart; - long mLastRealtime; - - /** - * These provide time bases that discount the time the device is plugged - * in to power. - */ - boolean mOnBattery = true; - long mTrackBatteryPastUptime = 0; - long mTrackBatteryUptimeStart = 0; - long mTrackBatteryPastRealtime = 0; - long mTrackBatteryRealtimeStart = 0; - - /** - * State for keeping track of timing information. - */ - final static class Timer { - long totalTime; - long startTime; - int nesting; - int count; - long loadedTotalTime; - int loadedCount; - long lastTotalTime; - int lastCount; - - void startRunningLocked(BatteryStats stats) { - if (nesting == 0) { - nesting = 1; - startTime = stats.getBatteryUptimeLocked(); - count++; - } else { - nesting++; - } - } - - void stopRunningLocked(BatteryStats stats) { - if (nesting == 1) { - nesting = 0; - long heldTime = stats.getBatteryUptimeLocked() - startTime; - if (heldTime != 0) { - totalTime += heldTime; - } else { - count--; - } - } else { - nesting--; - } - } - - long computeRunTimeLocked(long curTime) { - return totalTime + (nesting > 0 ? (curTime-startTime) : 0); - } - - void writeLocked(Parcel out, long curTime) throws java.io.IOException { - long runTime = computeRunTimeLocked(curTime); - out.writeLong(runTime); - out.writeLong(runTime - loadedTotalTime); - out.writeInt(count); - out.writeInt(count - loadedCount); - } - - void readLocked(Parcel in) throws java.io.IOException { - totalTime = loadedTotalTime = in.readLong(); - lastTotalTime = in.readLong(); - count = loadedCount = in.readInt(); - lastCount = in.readInt(); - nesting = 0; - } - } - - /** - * The statistics associated with a particular uid. - */ - final class Uid { - /** - * The statics we have collected for this uid's wake locks. - */ - final HashMap<String, Wakelock> wakelockStats = new HashMap<String, Wakelock>(); - - /** - * The statics we have collected for this uid's processes. - */ - final HashMap<String, Proc> processStats = new HashMap<String, Proc>(); - - /** - * The statics we have collected for this uid's processes. - */ - final HashMap<String, Pkg> packageStats = new HashMap<String, Pkg>(); - - /** - * The statistics associated with a particular wake lock. - */ - final class Wakelock { - /** - * How long (in ms) this uid has been keeping the device partially awake. - */ - Timer wakeTimePartial; - - /** - * How long (in ms) this uid has been keeping the device fully awake. - */ - Timer wakeTimeFull; - - /** - * How long (in ms) this uid has had a window keeping the device awake. - */ - Timer wakeTimeWindow; - } - - /** - * The statistics associated with a particular process. - */ - final class Proc { - /** - * Total time (in 1/100 sec) spent executing in user code. - */ - long userTime; - - /** - * Total time (in 1/100 sec) spent executing in kernel code. - */ - long systemTime; - - /** - * Number of times the process has been started. - */ - int starts; - - /** - * The amount of user time loaded from a previous save. - */ - long loadedUserTime; - - /** - * The amount of system time loaded from a previous save. - */ - long loadedSystemTime; - - /** - * The number of times the process has started from a previous save. - */ - int loadedStarts; - - /** - * The amount of user time loaded from the previous run. - */ - long lastUserTime; - - /** - * The amount of system time loaded from the previous run. - */ - long lastSystemTime; - - /** - * The number of times the process has started from the previous run. - */ - int lastStarts; - - BatteryStats getBatteryStats() { - return BatteryStats.this; - } - } - - /** - * The statistics associated with a particular package. - */ - final class Pkg { - /** - * Number of times this package has done something that could wake up the - * device from sleep. - */ - int wakeups; - - /** - * Number of things that could wake up the device loaded from a - * previous save. - */ - int loadedWakeups; - - /** - * Number of things that could wake up the device as of the - * last run. - */ - int lastWakeups; - - /** - * The statics we have collected for this package's services. - */ - final HashMap<String, Serv> serviceStats = new HashMap<String, Serv>(); - - /** - * The statistics associated with a particular service. - */ - final class Serv { - /** - * Total time (ms) the service has been left started. - */ - long startTime; - - /** - * If service has been started and not yet stopped, this is - * when it was started. - */ - long runningSince; - - /** - * True if we are currently running. - */ - boolean running; - - /** - * Total number of times startService() has been called. - */ - int starts; - - /** - * Total time (ms) the service has been left launched. - */ - long launchedTime; - - /** - * If service has been launched and not yet exited, this is - * when it was launched. - */ - long launchedSince; - - /** - * True if we are currently launched. - */ - boolean launched; - - /** - * Total number times the service has been launched. - */ - int launches; - - /** - * The amount of time spent started loaded from a previous save. - */ - long loadedStartTime; - - /** - * The number of starts loaded from a previous save. - */ - int loadedStarts; - - /** - * The number of launches loaded from a previous save. - */ - int loadedLaunches; - - /** - * The amount of time spent started as of the last run. - */ - long lastStartTime; - - /** - * The number of starts as of the last run. - */ - int lastStarts; - - /** - * The number of launches as of the last run. - */ - int lastLaunches; - - long getLaunchTimeToNowLocked(long now) { - if (!launched) return launchedTime; - return launchedTime + now - launchedSince; - } - - long getStartTimeToNowLocked(long now) { - if (!running) return startTime; - return startTime + now - runningSince; - } - - void startLaunchedLocked() { - if (!launched) { - launches++; - launchedSince = getBatteryUptimeLocked(); - launched = true; - } - } - - void stopLaunchedLocked() { - if (launched) { - long time = getBatteryUptimeLocked() - launchedSince; - if (time > 0) { - launchedTime += time; - } else { - launches--; - } - launched = false; - } - } - - void startRunningLocked() { - if (!running) { - starts++; - runningSince = getBatteryUptimeLocked(); - running = true; - } - } - - void stopRunningLocked() { - if (running) { - long time = getBatteryUptimeLocked() - runningSince; - if (time > 0) { - startTime += time; - } else { - starts--; - } - running = false; - } - } - - BatteryStats getBatteryStats() { - return BatteryStats.this; - } - } - - BatteryStats getBatteryStats() { - return BatteryStats.this; - } - - private final Serv newServiceStatsLocked() { - return new Serv(); - } - } - - /** - * Retrieve the statistics object for a particular process, creating - * if needed. - */ - Proc getProcessStatsLocked(String name) { - Proc ps = processStats.get(name); - if (ps == null) { - ps = new Proc(); - processStats.put(name, ps); - } - - return ps; - } - - /** - * Retrieve the statistics object for a particular service, creating - * if needed. - */ - Pkg getPackageStatsLocked(String name) { - Pkg ps = packageStats.get(name); - if (ps == null) { - ps = new Pkg(); - packageStats.put(name, ps); - } - - return ps; - } - - /** - * Retrieve the statistics object for a particular service, creating - * if needed. - */ - Pkg.Serv getServiceStatsLocked(String pkg, String serv) { - Pkg ps = getPackageStatsLocked(pkg); - Pkg.Serv ss = ps.serviceStats.get(serv); - if (ss == null) { - ss = ps.newServiceStatsLocked(); - ps.serviceStats.put(serv, ss); - } - - return ss; - } - - Timer getWakeTimerLocked(String name, int type) { - Wakelock wl = wakelockStats.get(name); - if (wl == null) { - wl = new Wakelock(); - wakelockStats.put(name, wl); - } - Timer t = null; - switch (type) { - case WAKE_TYPE_PARTIAL: - t = wl.wakeTimePartial; - if (t == null) { - t = new Timer(); - wl.wakeTimePartial = t; - } - return t; - case WAKE_TYPE_FULL: - t = wl.wakeTimeFull; - if (t == null) { - t = new Timer(); - wl.wakeTimeFull = t; - } - return t; - case WAKE_TYPE_WINDOW: - t = wl.wakeTimeWindow; - if (t == null) { - t = new Timer(); - wl.wakeTimeWindow = t; - } - return t; - } - return t; - } - - void noteStartWakeLocked(String name, int type) { - Timer t = getWakeTimerLocked(name, type); - if (t != null) { - t.startRunningLocked(BatteryStats.this); - } - } - - void noteStopWakeLocked(String name, int type) { - Timer t = getWakeTimerLocked(name, type); - if (t != null) { - t.stopRunningLocked(BatteryStats.this); - } - } - - BatteryStats getBatteryStats() { - return BatteryStats.this; - } - } - - BatteryStats(String filename) { - mFile = new File(filename); - mBackupFile = new File(filename + ".bak"); - mStartCount++; - mUptimeStart = SystemClock.uptimeMillis(); - mRealtimeStart = SystemClock.elapsedRealtime(); - } - - public void publish(Context context) { - mContext = context; - ServiceManager.addService("batteryinfo", asBinder()); - } - - public static IBatteryStats getService() { - if (sService != null) { - return sService; - } - IBinder b = ServiceManager.getService("batteryinfo"); - sService = asInterface(b); - return sService; - } - - public void noteStartWakelock(int uid, String name, int type) { - enforceCallingPermission(); - synchronized(this) { - getUidStatsLocked(uid).noteStartWakeLocked(name, type); - } - } - - public void noteStopWakelock(int uid, String name, int type) { - enforceCallingPermission(); - synchronized(this) { - getUidStatsLocked(uid).noteStopWakeLocked(name, type); - } - } - - public boolean isOnBattery() { - return mOnBattery; - } - - public void setOnBattery(boolean onBattery) { - enforceCallingPermission(); - synchronized(this) { - if (mOnBattery != onBattery) { - if (onBattery) { - mTrackBatteryUptimeStart = SystemClock.uptimeMillis(); - mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime(); - } else { - mTrackBatteryPastUptime += - SystemClock.uptimeMillis() - mTrackBatteryUptimeStart; - mTrackBatteryPastRealtime += - SystemClock.elapsedRealtime() - mTrackBatteryRealtimeStart; - } - mOnBattery = onBattery; - } - } - } - - public long getAwakeTimeBattery() { - return computeBatteryUptime(getBatteryUptimeLocked(), STATS_CURRENT); - } - - public long getAwakeTimePlugged() { - return SystemClock.uptimeMillis() - getAwakeTimeBattery(); - } - - long getBatteryUptimeLocked() { - long time = mTrackBatteryPastUptime; - if (mOnBattery) { - time += SystemClock.uptimeMillis() - mTrackBatteryUptimeStart; - } - return time; - } - - long getBatteryRealtimeLocked() { - long time = mTrackBatteryPastRealtime; - if (mOnBattery) { - time += SystemClock.elapsedRealtime() - mTrackBatteryRealtimeStart; - } - return time; - } - - long computeUptime(long curTime, int which) { - switch (which) { - case STATS_LOADED: return mUptime + (curTime-mUptimeStart); - case STATS_LAST: return mLastUptime; - case STATS_CURRENT: return (curTime-mUptimeStart); - } - return 0; - } - - long computeRealtime(long curTime, int which) { - switch (which) { - case STATS_LOADED: return mRealtime + (curTime-mRealtimeStart); - case STATS_LAST: return mLastRealtime; - case STATS_CURRENT: return (curTime-mRealtimeStart); - } - return 0; - } - - long computeBatteryUptime(long curTime, int which) { - switch (which) { - case STATS_LOADED: return mBatteryUptime + (curTime-mBatteryUptimeStart); - case STATS_LAST: return mBatteryLastUptime; - case STATS_CURRENT: return (curTime-mBatteryUptimeStart); - } - return 0; - } - - long computeBatteryRealtime(long curTime, int which) { - switch (which) { - case STATS_LOADED: return mBatteryRealtime + (curTime-mBatteryRealtimeStart); - case STATS_LAST: return mBatteryLastRealtime; - case STATS_CURRENT: return (curTime-mBatteryRealtimeStart); - } - return 0; - } - - public void enforceCallingPermission() { - if (Binder.getCallingPid() == Process.myPid()) { - return; - } - mContext.enforcePermission(android.Manifest.permission.BATTERY_STATS, - Binder.getCallingPid(), Binder.getCallingUid(), null); - } - - /** - * Retrieve the statistics object for a particular uid, creating if needed. - */ - Uid getUidStatsLocked(int uid) { - Uid u = uidStats.get(uid); - if (u == null) { - u = new Uid(); - uidStats.put(uid, u); - } - return u; - } - - /** - * Retrieve the statistics object for a particular process, creating - * if needed. - */ - Uid.Proc getProcessStatsLocked(int uid, String name) { - Uid u = getUidStatsLocked(uid); - return u.getProcessStatsLocked(name); - } - - /** - * Retrieve the statistics object for a particular process, creating - * if needed. - */ - Uid.Pkg getPackageStatsLocked(int uid, String pkg) { - Uid u = getUidStatsLocked(uid); - return u.getPackageStatsLocked(pkg); - } - - /** - * Retrieve the statistics object for a particular service, creating - * if needed. - */ - Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name) { - Uid u = getUidStatsLocked(uid); - return u.getServiceStatsLocked(pkg, name); - } - - private final static String formatTime(long time) { - long sec = time/100; - StringBuilder sb = new StringBuilder(); - sb.append(sec); - if (time != 0) { - sb.append('.'); - sb.append((char)(((time/10)%10)+'0')); - sb.append((char)((time%10)+'0')); - } - sb.append(" sec"); - return sb.toString(); - } - - private final static String formatTimeMs(long time) { - long sec = time/1000; - StringBuilder sb = new StringBuilder(); - sb.append(sec); - if (time != 0) { - sb.append('.'); - sb.append((char)(((time/100)%10)+'0')); - sb.append((char)(((time/10)%10)+'0')); - sb.append((char)((time%10)+'0')); - } - sb.append(" sec"); - return sb.toString(); - } - - final String printWakeLock(StringBuilder sb, Timer timer, long now, - String name, int which, String linePrefix) { - if (timer != null) { - long totalTime; - int count; - if (which == STATS_LAST) { - totalTime = timer.lastTotalTime; - count = timer.lastCount; - } else { - totalTime = timer.computeRunTimeLocked(now); - count = timer.count; - if (which == STATS_CURRENT) { - totalTime -= timer.loadedTotalTime; - count -= timer.loadedCount; - } - } - if (totalTime != 0) { - sb.append(linePrefix); - sb.append(formatTimeMs(totalTime)); - sb.append(' '); - sb.append(name); - sb.append(' '); - sb.append('('); - sb.append(count); - sb.append(" times)"); - return ", "; - } - } - return linePrefix; - } - - final void dumpLocked(FileDescriptor fd, PrintWriter pw, String prefix, - int which) { - final long NOW = getBatteryUptimeLocked(); - - StringBuilder sb = new StringBuilder(); - if (which == STATS_LOADED) { - pw.println(prefix + "Current and Historic Battery Usage Statistics:"); - pw.println(prefix + " System starts: " + mStartCount); - } else if (which == STATS_LAST) { - pw.println(prefix + "Last Battery Usage Statistics:"); - } else { - pw.println(prefix + "Current Battery Usage Statistics:"); - } - pw.println(prefix - + " On battery: " - + formatTimeMs(computeBatteryUptime(NOW, which)) - + " uptime, " - + formatTimeMs(computeBatteryRealtime(getBatteryRealtimeLocked(), - which)) - + " realtime"); - pw.println(prefix - + " Total: " - + formatTimeMs(computeUptime(SystemClock.uptimeMillis(), which)) - + " uptime, " - + formatTimeMs(computeRealtime(SystemClock.elapsedRealtime(), - which)) - + " realtime"); - - pw.println(" "); - final int NU = uidStats.size(); - for (int iu=0; iu<NU; iu++) { - final int uid = uidStats.keyAt(iu); - Uid u = uidStats.valueAt(iu); - pw.println(prefix + " #" + uid + ":"); - boolean uidActivity = false; - if (u.wakelockStats.size() > 0) { - for (Map.Entry<String, BatteryStats.Uid.Wakelock> ent - : u.wakelockStats.entrySet()) { - Uid.Wakelock wl = ent.getValue(); - String linePrefix = ": "; - sb.setLength(0); - sb.append(prefix); - sb.append(" Wake lock "); - sb.append(ent.getKey()); - linePrefix = printWakeLock(sb, wl.wakeTimeFull, NOW, - "full", which, linePrefix); - linePrefix = printWakeLock(sb, wl.wakeTimePartial, NOW, - "partial", which, linePrefix); - linePrefix = printWakeLock(sb, wl.wakeTimeWindow, NOW, - "window", which, linePrefix); - if (linePrefix.equals(": ")) { - sb.append(": (nothing executed)"); - } - pw.println(sb.toString()); - uidActivity = true; - } - } - if (u.processStats.size() > 0) { - for (Map.Entry<String, BatteryStats.Uid.Proc> ent - : u.processStats.entrySet()) { - BatteryStats.Uid.Proc ps = ent.getValue(); - long userTime; - long systemTime; - int starts; - if (which == STATS_LAST) { - userTime = ps.lastUserTime; - systemTime = ps.lastSystemTime; - starts = ps.lastStarts; - } else { - userTime = ps.userTime; - systemTime = ps.systemTime; - starts = ps.starts; - if (which == STATS_CURRENT) { - userTime -= ps.loadedUserTime; - systemTime -= ps.loadedSystemTime; - starts -= ps.loadedStarts; - } - } - if (userTime != 0 || systemTime != 0 || starts != 0) { - pw.println(prefix + " Proc " + ent.getKey() + ":"); - pw.println(prefix + " CPU: " + formatTime(userTime) + " user + " - + formatTime(systemTime) + " kernel"); - pw.println(prefix + " " + starts + " process starts"); - uidActivity = true; - } - } - } - if (u.packageStats.size() > 0) { - for (Map.Entry<String, BatteryStats.Uid.Pkg> ent - : u.packageStats.entrySet()) { - pw.println(prefix + " Apk " + ent.getKey() + ":"); - boolean apkActivity = false; - BatteryStats.Uid.Pkg ps = ent.getValue(); - int wakeups; - if (which == STATS_LAST) { - wakeups = ps.lastWakeups; - } else { - wakeups = ps.wakeups; - if (which == STATS_CURRENT) { - wakeups -= ps.loadedWakeups; - } - } - if (wakeups != 0) { - pw.println(prefix + " " + wakeups + " wakeup alarms"); - apkActivity = true; - } - if (ps.serviceStats.size() > 0) { - for (Map.Entry<String, BatteryStats.Uid.Pkg.Serv> sent - : ps.serviceStats.entrySet()) { - BatteryStats.Uid.Pkg.Serv ss = sent.getValue(); - long time; - int starts; - int launches; - if (which == STATS_LAST) { - time = ss.lastStartTime; - starts = ss.lastStarts; - launches = ss.lastLaunches; - } else { - time = ss.getStartTimeToNowLocked(NOW); - starts = ss.starts; - launches = ss.launches; - if (which == STATS_CURRENT) { - time -= ss.loadedStartTime; - starts -= ss.loadedStarts; - launches -= ss.loadedLaunches; - } - } - if (time != 0 || starts != 0 || launches != 0) { - pw.println(prefix + " Service " + sent.getKey() + ":"); - pw.println(prefix + " Time spent started: " - + formatTimeMs(time)); - pw.println(prefix + " Starts: " + starts - + ", launches: " + launches); - apkActivity = true; - } - } - } - if (!apkActivity) { - pw.println(prefix + " (nothing executed)"); - } - uidActivity = true; - } - } - if (!uidActivity) { - pw.println(prefix + " (nothing executed)"); - } - } - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - synchronized (this) { - dumpLocked(fd, pw, "", STATS_LOADED); - pw.println(""); - dumpLocked(fd, pw, "", STATS_LAST); - pw.println(""); - dumpLocked(fd, pw, "", STATS_CURRENT); - } - } - - void writeLocked() { - // Keep the old file around until we know the new one has - // been successfully written. - if (mFile.exists()) { - if (mBackupFile.exists()) { - mBackupFile.delete(); - } - mFile.renameTo(mBackupFile); - } - - try { - FileOutputStream stream = new FileOutputStream(mFile); - - final long NOW = getBatteryUptimeLocked(); - final long NOWREAL = getBatteryRealtimeLocked(); - final long NOW_SYS = SystemClock.uptimeMillis(); - final long NOWREAL_SYS = SystemClock.elapsedRealtime(); - - Parcel out = Parcel.obtain(); - out.writeInt(VERSION); - out.writeInt(mStartCount); - out.writeLong(computeBatteryUptime(NOW, STATS_LOADED)); - out.writeLong(computeBatteryUptime(NOW, STATS_CURRENT)); - out.writeLong(computeBatteryRealtime(NOWREAL, STATS_LOADED)); - out.writeLong(computeBatteryRealtime(NOWREAL, STATS_CURRENT)); - out.writeLong(computeUptime(NOW_SYS, STATS_LOADED)); - out.writeLong(computeUptime(NOW_SYS, STATS_CURRENT)); - out.writeLong(computeRealtime(NOWREAL_SYS, STATS_LOADED)); - out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT)); - - final int NU = uidStats.size(); - out.writeInt(NU); - for (int iu=0; iu<NU; iu++) { - out.writeInt(uidStats.keyAt(iu)); - Uid u = uidStats.valueAt(iu); - int NW = u.wakelockStats.size(); - out.writeInt(NW); - if (NW > 0) { - for (Map.Entry<String, BatteryStats.Uid.Wakelock> ent - : u.wakelockStats.entrySet()) { - out.writeString(ent.getKey()); - Uid.Wakelock wl = ent.getValue(); - if (wl.wakeTimeFull != null) { - out.writeInt(1); - wl.wakeTimeFull.writeLocked(out, NOW); - } else { - out.writeInt(0); - } - if (wl.wakeTimePartial != null) { - out.writeInt(1); - wl.wakeTimePartial.writeLocked(out, NOW); - } else { - out.writeInt(0); - } - if (wl.wakeTimeWindow != null) { - out.writeInt(1); - wl.wakeTimeWindow.writeLocked(out, NOW); - } else { - out.writeInt(0); - } - } - } - - int NP = u.processStats.size(); - out.writeInt(NP); - if (NP > 0) { - for (Map.Entry<String, BatteryStats.Uid.Proc> ent - : u.processStats.entrySet()) { - out.writeString(ent.getKey()); - BatteryStats.Uid.Proc ps = ent.getValue(); - out.writeLong(ps.userTime); - out.writeLong(ps.userTime - ps.loadedUserTime); - out.writeLong(ps.systemTime); - out.writeLong(ps.systemTime - ps.loadedSystemTime); - out.writeInt(ps.starts); - out.writeInt(ps.starts - ps.loadedStarts); - } - } - - NP = u.packageStats.size(); - out.writeInt(NP); - if (NP > 0) { - for (Map.Entry<String, BatteryStats.Uid.Pkg> ent - : u.packageStats.entrySet()) { - out.writeString(ent.getKey()); - BatteryStats.Uid.Pkg ps = ent.getValue(); - out.writeInt(ps.wakeups); - out.writeInt(ps.wakeups - ps.loadedWakeups); - final int NS = ps.serviceStats.size(); - out.writeInt(NS); - if (NS > 0) { - for (Map.Entry<String, BatteryStats.Uid.Pkg.Serv> sent - : ps.serviceStats.entrySet()) { - out.writeString(sent.getKey()); - BatteryStats.Uid.Pkg.Serv ss = sent.getValue(); - long time = ss.getStartTimeToNowLocked(NOW); - out.writeLong(time); - out.writeLong(time - ss.loadedStartTime); - out.writeInt(ss.starts); - out.writeInt(ss.starts - ss.loadedStarts); - out.writeInt(ss.launches); - out.writeInt(ss.launches - ss.loadedLaunches); - } - } - } - } - } - - stream.write(out.marshall()); - out.recycle(); - - stream.flush(); - stream.close(); - mBackupFile.delete(); - - } catch(java.io.IOException e) { - Log.e("BatteryStats", "Error writing battery statistics", e); - - } - } - - static byte[] readFully(FileInputStream stream) throws java.io.IOException { - int pos = 0; - int avail = stream.available(); - byte[] data = new byte[avail]; - while (true) { - int amt = stream.read(data, pos, data.length-pos); - //Log.i("foo", "Read " + amt + " bytes at " + pos - // + " of avail " + data.length); - if (amt <= 0) { - //Log.i("foo", "**** FINISHED READING: pos=" + pos - // + " len=" + data.length); - return data; - } - pos += amt; - avail = stream.available(); - if (avail > data.length-pos) { - byte[] newData = new byte[pos+avail]; - System.arraycopy(data, 0, newData, 0, pos); - data = newData; - } - } - } - - void readLocked() { - uidStats.clear(); - - FileInputStream stream = null; - if (mBackupFile.exists()) { - try { - stream = new FileInputStream(mBackupFile); - } catch (java.io.IOException e) { - // We'll try for the normal settings file. - } - } - - try { - if (stream == null) { - if (!mFile.exists()) { - return; - } - stream = new FileInputStream(mFile); - } - - byte[] raw = readFully(stream); - Parcel in = Parcel.obtain(); - in.unmarshall(raw, 0, raw.length); - in.setDataPosition(0); - - stream.close(); - - final int version = in.readInt(); - //Log.i("foo", "Read version: got " + version + ", expecting " + VERSION); - if (version != VERSION) { - return; - } - - mStartCount = in.readInt(); - mBatteryUptime = in.readLong(); - mBatteryLastUptime = in.readLong(); - mBatteryRealtime = in.readLong(); - mBatteryLastRealtime = in.readLong(); - mUptime = in.readLong(); - mLastUptime = in.readLong(); - mRealtime = in.readLong(); - mLastRealtime = in.readLong(); - //Log.i("foo", "Start count: " + mStartCount); - mStartCount++; - - final int NU = in.readInt(); - //Log.i("foo", "Number uids: " + NU); - for (int iu=0; iu<NU; iu++) { - int uid = in.readInt(); - //Log.i("foo", "Uid #" + iu + ": " + uid); - Uid u = new Uid(); - uidStats.put(uid, u); - int NW = in.readInt(); - for (int iw=0; iw<NW; iw++) { - String wlName = in.readString(); - if (in.readInt() != 0) { - u.getWakeTimerLocked(wlName, WAKE_TYPE_FULL).readLocked(in); - } - if (in.readInt() != 0) { - u.getWakeTimerLocked(wlName, WAKE_TYPE_PARTIAL).readLocked(in); - } - if (in.readInt() != 0) { - u.getWakeTimerLocked(wlName, WAKE_TYPE_WINDOW).readLocked(in); - } - } - - int NP = in.readInt(); - for (int ip=0; ip<NP; ip++) { - String procName = in.readString(); - Uid.Proc p = u.getProcessStatsLocked(procName); - p.userTime = p.loadedUserTime = in.readLong(); - p.lastUserTime = in.readLong(); - p.systemTime = p.loadedSystemTime = in.readLong(); - p.lastSystemTime = in.readLong(); - p.starts = p.loadedStarts = in.readInt(); - p.lastStarts = in.readInt(); - } - - NP = in.readInt(); - for (int ip=0; ip<NP; ip++) { - String pkgName = in.readString(); - Uid.Pkg p = u.getPackageStatsLocked(pkgName); - p.wakeups = p.loadedWakeups = in.readInt(); - p.lastWakeups = in.readInt(); - final int NS = in.readInt(); - for (int is=0; is<NS; is++) { - String servName = in.readString(); - Uid.Pkg.Serv s = u.getServiceStatsLocked(pkgName, servName); - s.startTime = s.loadedStartTime = in.readLong(); - s.lastStartTime = in.readLong(); - s.starts = s.loadedStarts = in.readInt(); - s.lastStarts = in.readInt(); - s.launches = s.loadedLaunches = in.readInt(); - s.lastLaunches = in.readInt(); - } - } - } - - } catch(java.io.IOException e) { - Log.e("BatteryStats", "Error reading battery statistics", e); - } - } -} diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java new file mode 100644 index 0000000..bf1bc8c --- /dev/null +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2006-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.am; + +import com.android.internal.app.IBatteryStats; +import com.android.internal.os.BatteryStatsImpl; + +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.Process; +import android.os.ServiceManager; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * All information we are collecting about things that can happen that impact + * battery life. + */ +public final class BatteryStatsService extends IBatteryStats.Stub { + static IBatteryStats sService; + + final BatteryStatsImpl mStats; + Context mContext; + + BatteryStatsService(String filename) { + mStats = new BatteryStatsImpl(filename); + } + + public void publish(Context context) { + mContext = context; + ServiceManager.addService("batteryinfo", asBinder()); + } + + public static IBatteryStats getService() { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService("batteryinfo"); + sService = asInterface(b); + return sService; + } + + /** + * @return the current statistics object, which may be modified + * to reflect events that affect battery usage. + */ + public BatteryStatsImpl getActiveStatistics() { + return mStats; + } + + public BatteryStatsImpl getStatistics() { + return mStats; + } + + public void noteStartWakelock(int uid, String name, int type) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type); + } + } + + public void noteStopWakelock(int uid, String name, int type) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type); + } + } + + public void noteStartSensor(int uid, int sensor) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.getUidStatsLocked(uid).noteStartSensor(sensor); + } + } + + public void noteStopSensor(int uid, int sensor) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.getUidStatsLocked(uid).noteStopSensor(sensor); + } + } + + public boolean isOnBattery() { + return mStats.isOnBattery(); + } + + public void setOnBattery(boolean onBattery) { + enforceCallingPermission(); + mStats.setOnBattery(onBattery); + } + + public long getAwakeTimeBattery() { + return mStats.getAwakeTimeBattery(); + } + + public long getAwakeTimePlugged() { + return mStats.getAwakeTimePlugged(); + } + + public void enforceCallingPermission() { + if (Binder.getCallingPid() == Process.myPid()) { + return; + } + mContext.enforcePermission(android.Manifest.permission.BATTERY_STATS, + Binder.getCallingPid(), Binder.getCallingUid(), null); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + synchronized (this) { + mStats.dumpLocked(fd, pw, args); + } + } +} diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index bf95f46..a1320df 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; import android.app.ActivityManager; @@ -38,10 +39,11 @@ import java.util.HashSet; * is currently running. */ class ProcessRecord implements Watchdog.PssRequestor { - final BatteryStats.Uid.Proc batteryStats; // where to collect runtime statistics + final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics final ApplicationInfo info; // all about the first app in the process final String processName; // name of the process - String uniquePackage; // Name of package if only one in the process + // List of packages running in the process + final HashSet<String> pkgList = new HashSet(); IApplicationThread thread; // the actual proc... may be null only if // 'persistent' is true (in which case we // are in the process of launching the app) @@ -107,7 +109,7 @@ class ProcessRecord implements Watchdog.PssRequestor { pw.println(prefix+"manageSpaceActivityName="+info.manageSpaceActivityName); pw.println(prefix + "dir=" + info.sourceDir + " publicDir=" + info.publicSourceDir + " data=" + info.dataDir); - pw.println(prefix + "uniquePackage=" + uniquePackage); + pw.println(prefix + "packageList=" + pkgList); pw.println(prefix + "instrumentationClass=" + instrumentationClass + " instrumentationProfileFile=" + instrumentationProfileFile); pw.println(prefix + "instrumentationArguments=" + instrumentationArguments); @@ -136,12 +138,12 @@ class ProcessRecord implements Watchdog.PssRequestor { pw.println(prefix + "receivers=" + receivers); } - ProcessRecord(BatteryStats.Uid.Proc _batteryStats, IApplicationThread _thread, + ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread, ApplicationInfo _info, String _processName) { batteryStats = _batteryStats; info = _info; processName = _processName; - uniquePackage = _info.packageName; + pkgList.add(_info.packageName); thread = _thread; maxAdj = ActivityManagerService.EMPTY_APP_ADJ; hiddenAdj = ActivityManagerService.HIDDEN_APP_MIN_ADJ; @@ -190,4 +192,33 @@ class ProcessRecord implements Watchdog.PssRequestor { + Integer.toHexString(System.identityHashCode(this)) + " " + pid + ":" + processName + "/" + info.uid + "}"; } + + /* + * Return true if package has been added false if not + */ + public boolean addPackage(String pkg) { + if (!pkgList.contains(pkg)) { + pkgList.add(pkg); + return true; + } + return false; + } + + /* + * Delete all packages from list except the package indicated in info + */ + public void resetPackageList() { + pkgList.clear(); + pkgList.add(info.packageName); + } + + public String[] getPackageList() { + int size = pkgList.size(); + if (size == 0) { + return null; + } + String list[] = new String[size]; + pkgList.toArray(list); + return list; + } } diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 55644a6..4b90600 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -16,6 +16,8 @@ package com.android.server.am; +import com.android.internal.os.BatteryStatsImpl; + import android.content.ComponentName; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -34,7 +36,7 @@ import java.util.List; * A running application service. */ class ServiceRecord extends Binder { - final BatteryStats.Uid.Pkg.Serv stats; + final BatteryStatsImpl.Uid.Pkg.Serv stats; final ComponentName name; // service component. final String shortName; // name.flattenToShortString(). final Intent.FilterComparison intent; @@ -114,7 +116,7 @@ class ServiceRecord extends Binder { } } - ServiceRecord(BatteryStats.Uid.Pkg.Serv servStats, ComponentName name, + ServiceRecord(BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name, Intent.FilterComparison intent, ServiceInfo sInfo, Runnable restarter) { this.stats = servStats; this.name = name; diff --git a/services/java/com/android/server/status/DateView.java b/services/java/com/android/server/status/DateView.java index 35a0bae..7c44d67 100644 --- a/services/java/com/android/server/status/DateView.java +++ b/services/java/com/android/server/status/DateView.java @@ -4,7 +4,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.pim.DateFormat; +import android.text.format.DateFormat; import android.util.AttributeSet; import android.util.Log; import android.widget.TextView; diff --git a/services/java/com/android/server/status/StatusBarIcon.java b/services/java/com/android/server/status/StatusBarIcon.java index 6c66f9d..6d09919 100644 --- a/services/java/com/android/server/status/StatusBarIcon.java +++ b/services/java/com/android/server/status/StatusBarIcon.java @@ -152,9 +152,9 @@ class StatusBarIcon { try { return r.getDrawable(data.iconId); } catch (RuntimeException e) { - Log.e(StatusBarService.TAG, "Icon not found in " + Log.w(StatusBarService.TAG, "Icon not found in " + (data.iconPackage != null ? data.iconId : "<system>") - + ": " + Integer.toHexString(data.iconId), e); + + ": " + Integer.toHexString(data.iconId)); } return null; diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index ad027db..00ff7be 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -22,6 +22,7 @@ import com.android.internal.telephony.SimCard; import com.android.internal.telephony.TelephonyIntents; import android.app.AlertDialog; +import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothIntent; @@ -39,11 +40,11 @@ import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.pim.DateFormat; import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.TelephonyManager; +import android.text.format.DateFormat; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -154,6 +155,8 @@ public class StatusBarPolicy { // bluetooth device status private IBinder mBluetoothIcon; private IconData mBluetoothData; + private int mBluetoothHeadsetState; + private int mBluetoothA2dpState; // wifi private static final int[] sWifiSignalImages = new int[] { @@ -196,6 +199,9 @@ public class StatusBarPolicy { else if (action.equals(Intent.ACTION_TIME_CHANGED)) { updateClock(); } + else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { + updateClock(); + } else if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { String tz = intent.getStringExtra("time-zone"); mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz)); @@ -212,7 +218,8 @@ public class StatusBarPolicy { } else if (action.equals(BluetoothIntent.ENABLED_ACTION) || action.equals(BluetoothIntent.DISABLED_ACTION) || - action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION) ) { + action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION) || + action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) { updateBluetooth(intent); } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) || @@ -315,6 +322,7 @@ public class StatusBarPolicy { // Register for Intent broadcasts for... filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); filter.addAction(Intent.ACTION_ALARM_CHANGED); @@ -324,7 +332,9 @@ public class StatusBarPolicy { filter.addAction(BluetoothIntent.ENABLED_ACTION); filter.addAction(BluetoothIntent.DISABLED_ACTION); filter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION); + filter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); filter.addAction(WifiManager.RSSI_CHANGED_ACTION); filter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION); @@ -652,7 +662,11 @@ public class StatusBarPolicy { return; } - if (-1 == asu) asu = 0; + // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5 + // asu = 0 (-113dB or less) is very weak + // signal, its better to show 0 bars to the user in such cases. + // asu = 99 is a special case, where the signal strength is unknown. + if (asu <= 0 || asu == 99) asu = 0; else if (asu >= 16) asu = 4; else if (asu >= 8) asu = 3; else if (asu >= 4) asu = 2; @@ -753,10 +767,13 @@ public class StatusBarPolicy { } private final void updateBluetooth(Intent intent) { - boolean visible; + boolean visible = false; if (intent == null) { // Initialize - visible = ((BluetoothDevice) - mContext.getSystemService(Context.BLUETOOTH_SERVICE)).isEnabled(); + BluetoothDevice bluetooth = + (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE); + if (bluetooth != null) { + visible = bluetooth.isEnabled(); + } mService.setIconVisibility(mBluetoothIcon, visible); return; } @@ -770,14 +787,21 @@ public class StatusBarPolicy { visible = true; } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) { visible = true; - int state = intent.getIntExtra(BluetoothIntent.HEADSET_STATE, - BluetoothHeadset.STATE_ERROR); - if (state == BluetoothHeadset.STATE_CONNECTED) { - iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected; - } + mBluetoothHeadsetState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE, + BluetoothHeadset.STATE_ERROR); + } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) { + visible = true; + mBluetoothA2dpState = intent.getIntExtra(BluetoothA2dp.SINK_STATE, + BluetoothA2dp.STATE_DISCONNECTED); } else { return; } + + if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || + mBluetoothA2dpState == BluetoothA2dp.STATE_CONNECTED || + mBluetoothA2dpState == BluetoothA2dp.STATE_PLAYING) { + iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected; + } mBluetoothData.iconId = iconId; mService.updateIcon(mBluetoothIcon, mBluetoothData, null); @@ -796,8 +820,14 @@ public class StatusBarPolicy { mService.setIconVisibility(mWifiIcon, false); } + } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) { + final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, + false); + if (!enabled) { + mService.setIconVisibility(mWifiIcon, false); + } } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - + final NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 38b2e77..31f55c8 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -147,10 +147,11 @@ public class StatusBarService extends IStatusBar.Stub final Context mContext; final Display mDisplay; StatusBarView mStatusBarView; + int mPixelFormat; H mHandler = new H(); ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>(); NotificationCallbacks mNotificationCallbacks; - + // All accesses to mIconMap and mNotificationData are syncronized on those objects, // but this is only so dump() can work correctly. Modifying these outside of the UI // thread will not work, there are places in the code that unlock and reaquire between @@ -181,7 +182,7 @@ public class StatusBarService extends IStatusBar.Stub View mNoNotificationsTitle; TextView mSpnLabel; TextView mPlmnLabel; - View mClearButton; + TextView mClearButton; CloseDragHandle mCloseView; int[] mCloseLocation = new int[2]; boolean mExpanded; @@ -252,10 +253,10 @@ public class StatusBarService extends IStatusBar.Stub sb.mService = this; // figure out which pixel-format to use for the status bar. - int pixelFormat = PixelFormat.TRANSLUCENT; + mPixelFormat = PixelFormat.TRANSLUCENT; Drawable bg = sb.getBackground(); if (bg != null) { - pixelFormat = bg.getOpacity(); + mPixelFormat = bg.getOpacity(); } mStatusBarView = sb; @@ -273,7 +274,7 @@ public class StatusBarService extends IStatusBar.Stub mLatestTitle = expanded.findViewById(R.id.latestTitle); mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems); mNoNotificationsTitle = expanded.findViewById(R.id.noNotificationsTitle); - mClearButton = expanded.findViewById(R.id.clear_all_button); + mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button); mClearButton.setOnClickListener(mClearButtonListener); mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel); mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel); @@ -294,18 +295,6 @@ public class StatusBarService extends IStatusBar.Stub mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close); mCloseView.mService = this; - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - 25, - WindowManager.LayoutParams.TYPE_STATUS_BAR, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| - WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING, - pixelFormat); - lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; - lp.setTitle("StatusBar"); - - WindowManagerImpl.getDefault().addView(sb, lp); - // add the more icon for the notifications IconData moreData = IconData.makeIcon(null, context.getPackageName(), R.drawable.stat_notify_more, 0, 42); @@ -326,11 +315,26 @@ public class StatusBarService extends IStatusBar.Stub // receive broadcasts IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION); context.registerReceiver(mBroadcastReceiver, filter); } + public void systemReady() { + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + 25, + WindowManager.LayoutParams.TYPE_STATUS_BAR, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| + WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING, + mPixelFormat); + lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + lp.setTitle("StatusBar"); + + WindowManagerImpl.getDefault().addView(mStatusBarView, lp); + } + // ================================================================================ // From IStatusBar // ================================================================================ @@ -665,7 +669,12 @@ public class StatusBarService extends IStatusBar.Stub notification.data = n; updateNotificationView(notification, oldData); } - if (n.tickerText != null + // Show the ticker if one is requested, and the text is different + // than the currently displayed ticker. Also don't do this + // until status bar window is attached to the window manager, + // because... well, what's the point otherwise? And trying to + // run a ticker without being attached will crash! + if (n.tickerText != null && mStatusBarView.getWindowToken() != null && (oldData == null || oldData.tickerText == null || !CharSequences.equals(oldData.tickerText, n.tickerText))) { @@ -788,6 +797,14 @@ public class StatusBarService extends IStatusBar.Stub } } + View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() { + public void onFocusChange(View v, boolean hasFocus) { + // Because 'v' is a ViewGroup, all its children will be (un)selected + // too, which allows marqueeing to work. + v.setSelected(hasFocus); + } + }; + View makeNotificationView(StatusBarNotification notification, ViewGroup parent) { NotificationData n = notification.data; RemoteViews remoteViews = n.contentView; @@ -803,6 +820,7 @@ public class StatusBarService extends IStatusBar.Stub // bind the click event to the content area ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content); content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + content.setOnFocusChangeListener(mFocusChangeListener); PendingIntent contentIntent = n.contentIntent; if (contentIntent != null) { content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.id)); @@ -862,18 +880,19 @@ public class StatusBarService extends IStatusBar.Stub mNotificationData.update(notification); try { n.contentView.reapply(mContext, notification.contentView); + + // update the contentIntent + ViewGroup content = (ViewGroup)notification.view.findViewById( + com.android.internal.R.id.content); + PendingIntent contentIntent = n.contentIntent; + if (contentIntent != null) { + content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.id)); + } } catch (RuntimeException e) { - Log.e(TAG, "couldn't reapply views for package " - + n.contentView.getPackage(), e); - } - - // update the contentIntent - ViewGroup content = (ViewGroup)notification.view.findViewById( - com.android.internal.R.id.content); - PendingIntent contentIntent = n.contentIntent; - if (contentIntent != null) { - content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.id)); + // It failed to add cleanly. Log, and remove the view from the panel. + Log.w(TAG, "couldn't reapply views for package " + n.contentView.getPackage(), e); + removeNotificationView(notification); } } else { mNotificationData.update(notification); @@ -1426,7 +1445,8 @@ public class StatusBarService extends IStatusBar.Stub ViewGroup.LayoutParams.FILL_PARENT, WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, pixelFormat); // lp.token = mStatusBarView.getWindowToken(); lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; @@ -1629,6 +1649,9 @@ public class StatusBarService extends IStatusBar.Stub intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false), intent.getStringExtra(Telephony.Intents.EXTRA_PLMN)); } + else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { + updateResources(); + } } }; @@ -1659,6 +1682,18 @@ public class StatusBarService extends IStatusBar.Stub } } + /** + * Reload some of our resources when the configuration changes. + * + * We don't reload everything when the configuration changes -- we probably + * should, but getting that smooth is tough. Someday we'll fix that. In the + * meantime, just update the things that we know change. + */ + void updateResources() { + mClearButton.setText(mContext.getText(R.string.status_bar_clear_all_button)); + Log.d(TAG, "updateResources"); + } + // // tracing // |