diff options
Diffstat (limited to 'core/java')
31 files changed, 3381 insertions, 142 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4e6c3dc..c65f17e 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -16,14 +16,13 @@ package android.app; -import android.R; import android.os.BatteryStats; import android.os.IBinder; import com.android.internal.app.IUsageStats; +import com.android.internal.app.ProcessStats; import com.android.internal.os.PkgUsageStats; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; -import com.android.internal.util.MemInfoReader; import android.content.ComponentName; import android.content.Context; @@ -35,9 +34,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Point; import android.graphics.Rect; -import android.hardware.display.DisplayManagerGlobal; import android.os.Bundle; import android.os.Debug; import android.os.Handler; @@ -52,7 +49,6 @@ import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; -import android.view.Display; import java.io.FileDescriptor; import java.io.FileOutputStream; @@ -2253,7 +2249,7 @@ public class ActivityManager { PrintWriter pw = new FastPrintWriter(fout); dumpService(pw, fd, Context.ACTIVITY_SERVICE, new String[] { "package", packageName }); pw.println(); - dumpService(pw, fd, "procstats", new String[] { packageName }); + dumpService(pw, fd, ProcessStats.SERVICE_NAME, new String[] { packageName }); pw.println(); dumpService(pw, fd, "usagestats", new String[] { "--packages", packageName }); pw.println(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 9faaace..eb5c3e7 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1979,7 +1979,7 @@ class ContextImpl extends Context { " compatiblity info:" + container.getDisplayMetrics()); } if (compatInfo == null) { - compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + compatInfo = packageInfo.getCompatibilityInfo(); } mDisplayAdjustments.setCompatibilityInfo(compatInfo); mDisplayAdjustments.setActivityToken(activityToken); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 17e8dd9..be831d7 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1527,9 +1527,26 @@ public class DevicePolicyManager { */ public boolean setDeviceOwner(String packageName) throws IllegalArgumentException, IllegalStateException { + return setDeviceOwner(packageName, null); + } + + /** + * @hide + * Sets the given package as the device owner. The package must already be installed and there + * shouldn't be an existing device owner registered, for this call to succeed. Also, this + * method must be called before the device is provisioned. + * @param packageName the package name of the application to be registered as the device owner. + * @param ownerName the human readable name of the institution that owns this device. + * @return whether the package was successfully registered as the device owner. + * @throws IllegalArgumentException if the package name is null or invalid + * @throws IllegalStateException if a device owner is already registered or the device has + * already been provisioned. + */ + public boolean setDeviceOwner(String packageName, String ownerName) + throws IllegalArgumentException, IllegalStateException { if (mService != null) { try { - return mService.setDeviceOwner(packageName); + return mService.setDeviceOwner(packageName, ownerName); } catch (RemoteException re) { Log.w(TAG, "Failed to set device owner"); } @@ -1581,4 +1598,16 @@ public class DevicePolicyManager { } return null; } + + /** @hide */ + public String getDeviceOwnerName() { + if (mService != null) { + try { + return mService.getDeviceOwnerName(); + } catch (RemoteException re) { + Log.w(TAG, "Failed to get device owner"); + } + } + return null; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index b2a65bf..9659a91 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -98,7 +98,8 @@ interface IDevicePolicyManager { void reportFailedPasswordAttempt(int userHandle); void reportSuccessfulPasswordAttempt(int userHandle); - boolean setDeviceOwner(String packageName); + boolean setDeviceOwner(String packageName, String ownerName); boolean isDeviceOwner(String packageName); String getDeviceOwner(); + String getDeviceOwnerName(); } diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index 81c0a6a..0aedecb 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -152,6 +152,11 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { // not implemented } + @Override + public void captivePortalCheckCompleted(boolean isCaptivePortal) { + // not implemented + } + /** * Re-enable connectivity to a network after a {@link #teardown()}. */ diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 6318e38..1c28138 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -786,10 +786,14 @@ public final class Configuration implements Parcelable, Comparable<Configuration // 2 most significant bits in screenLayout). setLayoutDirection(locale); } + if (delta.screenLayout != 0 && screenLayout != delta.screenLayout) { + changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION; + setLayoutDirection(locale); + } if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0))) { - userSetLocale = true; changed |= ActivityInfo.CONFIG_LOCALE; + userSetLocale = true; } if (delta.touchscreen != TOUCHSCREEN_UNDEFINED && touchscreen != delta.touchscreen) { @@ -933,6 +937,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration changed |= ActivityInfo.CONFIG_LOCALE; changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION; } + if (delta.screenLayout != 0 && screenLayout != delta.screenLayout) { + changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION; + } if (delta.touchscreen != TOUCHSCREEN_UNDEFINED && touchscreen != delta.touchscreen) { changed |= ActivityInfo.CONFIG_TOUCHSCREEN; @@ -1005,7 +1012,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration public static boolean needNewResources(int configChanges, int interestingChanges) { return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0; } - + /** * @hide Return true if the sequence of 'other' is better than this. Assumes * that 'this' is your current sequence and 'other' is a new one you have diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java index a9e39c3..b234e34 100644..100755 --- a/core/java/android/database/DefaultDatabaseErrorHandler.java +++ b/core/java/android/database/DefaultDatabaseErrorHandler.java @@ -99,7 +99,7 @@ public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler { } Log.e(TAG, "deleting the database file: " + fileName); try { - new File(fileName).delete(); + SQLiteDatabase.deleteDatabase(new File(fileName)); } catch (Exception e) { /* print warning and ignore exception */ Log.w(TAG, "delete failed: " + e.getMessage()); diff --git a/core/java/android/hardware/location/IFusedLocationHardware.aidl b/core/java/android/hardware/location/IFusedLocationHardware.aidl new file mode 100644 index 0000000..382c12c --- /dev/null +++ b/core/java/android/hardware/location/IFusedLocationHardware.aidl @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2013, 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/license/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 android.hardware.location; + +import android.hardware.location.IFusedLocationHardwareSink; +import android.location.FusedBatchOptions; + +/** + * Fused Location hardware interface. + * This interface is the basic set of supported functionality by Fused Hardware + * modules that offer Location batching capabilities. + * + * @hide + */ +interface IFusedLocationHardware { + /** + * Registers a sink with the Location Hardware object. + * + * @param eventSink The sink to register. + */ + void registerSink(in IFusedLocationHardwareSink eventSink); + + /** + * Unregisters a sink with the Location Hardware object. + * + * @param eventSink The sink to unregister. + */ + void unregisterSink(in IFusedLocationHardwareSink eventSink); + + /** + * Provides access to the batch size available in Hardware. + * + * @return The batch size the hardware supports. + */ + int getSupportedBatchSize(); + + /** + * Requests the Hardware to start batching locations. + * + * @param id An Id associated with the request. + * @param batchOptions The options required for batching. + * + * @throws RuntimeException if the request Id exists. + */ + void startBatching(in int id, in FusedBatchOptions batchOptions); + + /** + * Requests the Hardware to stop batching for the given Id. + * + * @param id The request that needs to be stopped. + * @throws RuntimeException if the request Id is unknown. + */ + void stopBatching(in int id); + + /** + * Updates a batching operation in progress. + * + * @param id The Id of the operation to update. + * @param batchOptions The options to apply to the given operation. + * + * @throws RuntimeException if the Id of the request is unknown. + */ + void updateBatchingOptions(in int id, in FusedBatchOptions batchOptions); + + /** + * Requests the most recent locations available in Hardware. + * This operation does not dequeue the locations, so still other batching + * events will continue working. + * + * @param batchSizeRequested The number of locations requested. + */ + void requestBatchOfLocations(in int batchSizeRequested); + + /** + * Flags if the Hardware supports injection of diagnostic data. + * + * @return True if data injection is supported, false otherwise. + */ + boolean supportsDiagnosticDataInjection(); + + /** + * Injects diagnostic data into the Hardware subsystem. + * + * @param data The data to inject. + * @throws RuntimeException if injection is not supported. + */ + void injectDiagnosticData(in String data); + + /** + * Flags if the Hardware supports injection of device context information. + * + * @return True if device context injection is supported, false otherwise. + */ + boolean supportsDeviceContextInjection(); + + /** + * Injects device context information into the Hardware subsystem. + * + * @param deviceEnabledContext The context to inject. + * @throws RuntimeException if injection is not supported. + */ + void injectDeviceContext(in int deviceEnabledContext); +} diff --git a/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl b/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl new file mode 100644 index 0000000..a11d8ab --- /dev/null +++ b/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013, 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/license/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 android.hardware.location; + +import android.location.Location; + +/** + * Fused Location hardware event sink interface. + * This interface defines the set of events that the FusedLocationHardware provides. + * + * @hide + */ +interface IFusedLocationHardwareSink { + /** + * Event generated when a batch of location information is available. + * + * @param locations The batch of location information available. + */ + void onLocationAvailable(in Location[] locations); + + /** + * Event generated from FLP HAL to provide diagnostic data to the platform. + * + * @param data The diagnostic data provided by FLP HAL. + */ + void onDiagnosticDataAvailable(in String data); +}
\ No newline at end of file diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java index 1165281..e87f84c 100644 --- a/core/java/android/net/BaseNetworkStateTracker.java +++ b/core/java/android/net/BaseNetworkStateTracker.java @@ -102,6 +102,11 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { } @Override + public void captivePortalCheckCompleted(boolean isCaptivePortal) { + // not implemented + } + + @Override public boolean setRadio(boolean turnOn) { // Base tracker doesn't handle radios return true; diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java index 19d74ed..74c2c59 100644 --- a/core/java/android/net/CaptivePortalTracker.java +++ b/core/java/android/net/CaptivePortalTracker.java @@ -46,6 +46,7 @@ import android.telephony.CellInfoGsm; import android.telephony.CellInfoLte; import android.telephony.CellInfoWcdma; import android.telephony.TelephonyManager; +import android.text.TextUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -54,6 +55,7 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.Inet4Address; +import java.net.SocketTimeoutException; import java.net.URL; import java.net.UnknownHostException; import java.util.List; @@ -65,7 +67,7 @@ import com.android.internal.R; * @hide */ public class CaptivePortalTracker extends StateMachine { - private static final boolean DBG = false; + private static final boolean DBG = true; private static final String TAG = "CaptivePortalTracker"; private static final String DEFAULT_SERVER = "clients3.google.com"; @@ -301,6 +303,7 @@ public class CaptivePortalTracker extends StateMachine { } else { if (DBG) log("Not captive network " + mNetworkInfo); } + notifyPortalCheckCompleted(mNetworkInfo, captive); if (mDeviceProvisioned) { if (captive) { // Setup Wizard will assist the user in connecting to a captive @@ -331,12 +334,26 @@ public class CaptivePortalTracker extends StateMachine { return; } try { + if (DBG) log("notifyPortalCheckComplete: ni=" + info); mConnService.captivePortalCheckComplete(info); } catch(RemoteException e) { e.printStackTrace(); } } + private void notifyPortalCheckCompleted(NetworkInfo info, boolean isCaptivePortal) { + if (info == null) { + loge("notifyPortalCheckComplete on null"); + return; + } + try { + if (DBG) log("notifyPortalCheckCompleted: captive=" + isCaptivePortal + " ni=" + info); + mConnService.captivePortalCheckCompleted(info, isCaptivePortal); + } catch(RemoteException e) { + e.printStackTrace(); + } + } + private boolean isActiveNetwork(NetworkInfo info) { try { NetworkInfo active = mConnService.getActiveNetworkInfo(); @@ -382,6 +399,12 @@ public class CaptivePortalTracker extends StateMachine { sendNetworkConditionsBroadcast(true /* response received */, isCaptivePortal, requestTimestamp, responseTimestamp); return isCaptivePortal; + } catch (SocketTimeoutException e) { + if (DBG) log("Probably a portal: exception " + e); + if (requestTimestamp != -1) { + sendFailedCaptivePortalCheckBroadcast(requestTimestamp); + } // else something went wrong with setting up the urlConnection + return true; } catch (IOException e) { if (DBG) log("Probably not a portal: exception " + e); if (requestTimestamp != -1) { @@ -415,6 +438,7 @@ public class CaptivePortalTracker extends StateMachine { private void setNotificationVisible(boolean visible) { // if it should be hidden and it is already hidden, then noop if (!visible && !mNotificationShown) { + if (DBG) log("setNotivicationVisible: false and not shown, so noop"); return; } @@ -426,12 +450,14 @@ public class CaptivePortalTracker extends StateMachine { CharSequence title; CharSequence details; int icon; + String url = null; switch (mNetworkInfo.getType()) { case ConnectivityManager.TYPE_WIFI: title = r.getString(R.string.wifi_available_sign_in, 0); details = r.getString(R.string.network_available_sign_in_detailed, mNetworkInfo.getExtraInfo()); icon = R.drawable.stat_notify_wifi_in_range; + url = mUrl; break; case ConnectivityManager.TYPE_MOBILE: title = r.getString(R.string.network_available_sign_in, 0); @@ -439,12 +465,24 @@ public class CaptivePortalTracker extends StateMachine { // name has been added to it details = mTelephonyManager.getNetworkOperatorName(); icon = R.drawable.stat_notify_rssi_in_range; + try { + url = mConnService.getMobileProvisioningUrl(); + if (TextUtils.isEmpty(url)) { + url = mConnService.getMobileRedirectedProvisioningUrl(); + } + } catch(RemoteException e) { + e.printStackTrace(); + } + if (TextUtils.isEmpty(url)) { + url = mUrl; + } break; default: title = r.getString(R.string.network_available_sign_in, 0); details = r.getString(R.string.network_available_sign_in_detailed, mNetworkInfo.getExtraInfo()); icon = R.drawable.stat_notify_rssi_in_range; + url = mUrl; break; } @@ -452,15 +490,17 @@ public class CaptivePortalTracker extends StateMachine { notification.when = 0; notification.icon = icon; notification.flags = Notification.FLAG_AUTO_CANCEL; - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl)); + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0); notification.tickerText = title; notification.setLatestEventInfo(mContext, title, details, notification.contentIntent); + if (DBG) log("setNotivicationVisible: make visible"); notificationManager.notify(NOTIFICATION_ID, 1, notification); } else { + if (DBG) log("setNotivicationVisible: cancel notification"); notificationManager.cancel(NOTIFICATION_ID, 1); } mNotificationShown = visible; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 1dbe34e..1b418fa 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1324,6 +1324,25 @@ public class ConnectivityManager { } /** + * Signal that the captive portal check on the indicated network + * is complete and whether its a captive portal or not. + * + * @param info the {@link NetworkInfo} object for the networkType + * in question. + * @param isCaptivePortal true/false. + * + * <p>This method requires the call to hold the permission + * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}. + * {@hide} + */ + public void captivePortalCheckCompleted(NetworkInfo info, boolean isCaptivePortal) { + try { + mService.captivePortalCheckCompleted(info, isCaptivePortal); + } catch (RemoteException e) { + } + } + + /** * Supply the backend messenger for a network tracker * * @param type NetworkType to set @@ -1401,7 +1420,7 @@ public class ConnectivityManager { } /** - * Get the carrier provisioning url. + * Get the mobile provisioning url. * {@hide} */ public String getMobileProvisioningUrl() { @@ -1411,4 +1430,16 @@ public class ConnectivityManager { } return null; } + + /** + * Get the mobile redirected provisioning url. + * {@hide} + */ + public String getMobileRedirectedProvisioningUrl() { + try { + return mService.getMobileRedirectedProvisioningUrl(); + } catch (RemoteException e) { + } + return null; + } } diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java index 15a81f3..ee738fd 100644 --- a/core/java/android/net/DummyDataStateTracker.java +++ b/core/java/android/net/DummyDataStateTracker.java @@ -120,10 +120,16 @@ public class DummyDataStateTracker implements NetworkStateTracker { return true; } + @Override public void captivePortalCheckComplete() { // not implemented } + @Override + public void captivePortalCheckCompleted(boolean isCaptivePortal) { + // not implemented + } + /** * Record the detailed state of a network, and if it is a * change from the previous state, send a notification to diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index 7b803a8..7999c66 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -280,6 +280,11 @@ public class EthernetDataTracker implements NetworkStateTracker { // not implemented } + @Override + public void captivePortalCheckCompleted(boolean isCaptivePortal) { + // not implemented + } + /** * Turn the wireless radio off for a network. * @param turnOn {@code true} to turn the radio on, {@code false} diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index b0f7fc6..992ec37 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -134,6 +134,8 @@ interface IConnectivityManager void captivePortalCheckComplete(in NetworkInfo info); + void captivePortalCheckCompleted(in NetworkInfo info, boolean isCaptivePortal); + void supplyMessenger(int networkType, in Messenger messenger); int findConnectionTypeForIface(in String iface); @@ -141,4 +143,6 @@ interface IConnectivityManager int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs, in ResultReceiver resultReceiver); String getMobileProvisioningUrl(); + + String getMobileRedirectedProvisioningUrl(); } diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 54273ee..e4fd312 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -40,6 +40,7 @@ import com.android.internal.util.AsyncChannel; import java.io.CharArrayWriter; import java.io.PrintWriter; +import java.util.concurrent.atomic.AtomicBoolean; /** * Track the state of mobile data connectivity. This is done by @@ -75,6 +76,8 @@ public class MobileDataStateTracker implements NetworkStateTracker { private Handler mHandler; private AsyncChannel mDataConnectionTrackerAc; + private AtomicBoolean mIsCaptivePortal = new AtomicBoolean(false); + /** * Create a new MobileDataStateTracker * @param netType the ConnectivityManager network type @@ -377,6 +380,15 @@ public class MobileDataStateTracker implements NetworkStateTracker { // not implemented } + @Override + public void captivePortalCheckCompleted(boolean isCaptivePortal) { + if (mIsCaptivePortal.getAndSet(isCaptivePortal) != isCaptivePortal) { + // Captive portal change enable/disable failing fast + setEnableFailFastMobileData( + isCaptivePortal ? DctConstants.ENABLED : DctConstants.DISABLED); + } + } + /** * Record the detailed state of a network, and if it is a * change from the previous state, send a notification to diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index cf77a1c..9ed7533 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -144,6 +144,11 @@ public interface NetworkStateTracker { public void captivePortalCheckComplete(); /** + * Captive portal check has completed + */ + public void captivePortalCheckCompleted(boolean isCaptive); + + /** * Turn the wireless radio off for a network. * @param turnOn {@code true} to turn the radio on, {@code false} */ diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 12646bd..38ffb96 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -155,6 +155,7 @@ public abstract class BatteryStats implements Parcelable { private static final String BATTERY_LEVEL_DATA = "lv"; private static final String WIFI_DATA = "wfl"; private static final String MISC_DATA = "m"; + private static final String HISTORY_DATA = "h"; private static final String SCREEN_BRIGHTNESS_DATA = "br"; private static final String SIGNAL_STRENGTH_TIME_DATA = "sgt"; private static final String SIGNAL_SCANNING_TIME_DATA = "sst"; @@ -2390,7 +2391,7 @@ public abstract class BatteryStats implements Parcelable { while (getNextHistoryLocked(rec)) { pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); pw.print(0); pw.print(','); - pw.print("h"); pw.print(','); + pw.print(HISTORY_DATA); pw.print(','); hprinter.printNextItemCheckin(pw, rec, now); pw.println(); } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 97ea99d..4d48fd4 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -17,10 +17,17 @@ package android.os; import android.util.Log; +import android.util.Slog; + +import libcore.io.ErrnoException; +import libcore.io.IoUtils; +import libcore.io.Libcore; +import libcore.io.OsConstants; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -58,9 +65,84 @@ public class FileUtils { /** Regular expression for safe filenames: no spaces or metacharacters */ private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+"); - public static native int setPermissions(String file, int mode, int uid, int gid); + /** + * Set owner and mode of of given {@link File}. + * + * @param mode to apply through {@code chmod} + * @param uid to apply through {@code chown}, or -1 to leave unchanged + * @param gid to apply through {@code chown}, or -1 to leave unchanged + * @return 0 on success, otherwise errno. + */ + public static int setPermissions(File path, int mode, int uid, int gid) { + return setPermissions(path.getAbsolutePath(), mode, uid, gid); + } + + /** + * Set owner and mode of of given path. + * + * @param mode to apply through {@code chmod} + * @param uid to apply through {@code chown}, or -1 to leave unchanged + * @param gid to apply through {@code chown}, or -1 to leave unchanged + * @return 0 on success, otherwise errno. + */ + public static int setPermissions(String path, int mode, int uid, int gid) { + try { + Libcore.os.chmod(path, mode); + } catch (ErrnoException e) { + Slog.w(TAG, "Failed to chmod(" + path + "): " + e); + return e.errno; + } + + if (uid >= 0 || gid >= 0) { + try { + Libcore.os.chown(path, uid, gid); + } catch (ErrnoException e) { + Slog.w(TAG, "Failed to chown(" + path + "): " + e); + return e.errno; + } + } + + return 0; + } + + /** + * Set owner and mode of of given {@link FileDescriptor}. + * + * @param mode to apply through {@code chmod} + * @param uid to apply through {@code chown}, or -1 to leave unchanged + * @param gid to apply through {@code chown}, or -1 to leave unchanged + * @return 0 on success, otherwise errno. + */ + public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) { + try { + Libcore.os.fchmod(fd, mode); + } catch (ErrnoException e) { + Slog.w(TAG, "Failed to fchmod(): " + e); + return e.errno; + } - public static native int getUid(String file); + if (uid >= 0 || gid >= 0) { + try { + Libcore.os.fchown(fd, uid, gid); + } catch (ErrnoException e) { + Slog.w(TAG, "Failed to fchown(): " + e); + return e.errno; + } + } + + return 0; + } + + /** + * Return owning UID of given path, otherwise -1. + */ + public static int getUid(String path) { + try { + return Libcore.os.stat(path).st_uid; + } catch (ErrnoException e) { + return -1; + } + } /** returns the FAT file system volume ID for the volume mounted * at the given mount point, or -1 for failure diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index ab0543d..cf9ddb3 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -100,12 +100,6 @@ public class Process { public static final int DRM_UID = 1019; /** - * Defines the GID for the group that allows write access to the SD card. - * @hide - */ - public static final int SDCARD_RW_GID = 1015; - - /** * Defines the UID/GID for the group that controls VPN services. * @hide */ @@ -130,11 +124,18 @@ public class Process { public static final int MEDIA_RW_GID = 1023; /** + * Access to installed package details + * @hide + */ + public static final int PACKAGE_INFO_GID = 1032; + + /** * Defines the start of a range of UIDs (and GIDs), going from this * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning * to applications. */ public static final int FIRST_APPLICATION_UID = 10000; + /** * Last of application-specific UIDs starting at * {@link #FIRST_APPLICATION_UID}. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index cbc6c5c..585115a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -33,6 +33,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.database.SQLException; +import android.location.LocationManager; import android.net.ConnectivityManager; import android.net.Uri; import android.net.wifi.WifiManager; @@ -754,6 +755,10 @@ public final class Settings { private static final String TAG = "Settings"; private static final boolean LOCAL_LOGV = false; + // Lock ensures that when enabling/disabling the master location switch, we don't end up + // with a partial enable/disable state in multi-threaded situations. + private static final Object mLocationSettingsLock = new Object(); + public static class SettingNotFoundException extends AndroidException { public SettingNotFoundException(String msg) { super(msg); @@ -4320,6 +4325,20 @@ public final class Settings { } /** + * Helper method for determining if the location master switch is enabled. + * @param cr the content resolver to use + * @return true if the master switch is enabled + * @hide + */ + public static final boolean isLocationMasterSwitchEnabled(ContentResolver cr) { + int uid = UserHandle.myUserId(); + synchronized (mLocationSettingsLock) { + return isLocationProviderEnabledForUser(cr, LocationManager.NETWORK_PROVIDER, uid) + || isLocationProviderEnabledForUser(cr, LocationManager.GPS_PROVIDER, uid); + } + } + + /** * Helper method for determining if a location provider is enabled. * @param cr the content resolver to use * @param provider the location provider to query @@ -4345,6 +4364,23 @@ public final class Settings { } /** + * Thread-safe method for enabling or disabling the location master switch. + * + * @param cr the content resolver to use + * @param enabled true if master switch should be enabled + * @hide + */ + public static final void setLocationMasterSwitchEnabled(ContentResolver cr, + boolean enabled) { + int uid = UserHandle.myUserId(); + synchronized (mLocationSettingsLock) { + setLocationProviderEnabledForUser(cr, LocationManager.GPS_PROVIDER, enabled, uid); + setLocationProviderEnabledForUser(cr, LocationManager.NETWORK_PROVIDER, enabled, + uid); + } + } + + /** * Thread-safe method for enabling or disabling a single location provider. * @param cr the content resolver to use * @param provider the location provider to enable or disable @@ -4354,16 +4390,18 @@ public final class Settings { */ public static final void setLocationProviderEnabledForUser(ContentResolver cr, String provider, boolean enabled, int userId) { - // to ensure thread safety, we write the provider name with a '+' or '-' - // and let the SettingsProvider handle it rather than reading and modifying - // the list of enabled providers. - if (enabled) { - provider = "+" + provider; - } else { - provider = "-" + provider; + synchronized (mLocationSettingsLock) { + // to ensure thread safety, we write the provider name with a '+' or '-' + // and let the SettingsProvider handle it rather than reading and modifying + // the list of enabled providers. + if (enabled) { + provider = "+" + provider; + } else { + provider = "-" + provider; + } + putStringForUser(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider, + userId); } - putStringForUser(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider, - userId); } } @@ -5641,6 +5679,12 @@ public final class Settings { public static final String SELINUX_STATUS = "selinux_status"; /** + * Developer setting to force RTL layout. + * @hide + */ + public static final String DEVELOPMENT_FORCE_RTL = "debug.force_rtl"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index e2035c2..596ca8c 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -19,6 +19,8 @@ package android.text; import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemProperties; +import android.provider.Settings; import android.text.style.AbsoluteSizeSpan; import android.text.style.AlignmentSpan; import android.text.style.BackgroundColorSpan; @@ -1743,8 +1745,10 @@ public class TextUtils { return View.LAYOUT_DIRECTION_RTL; } } - - return View.LAYOUT_DIRECTION_LTR; + // If forcing into RTL layout mode, return RTL as default, else LTR + return SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false) + ? View.LAYOUT_DIRECTION_RTL + : View.LAYOUT_DIRECTION_LTR; } /** diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 6b530ef..dc31e0b 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -59,13 +59,14 @@ public class SurfaceControl { private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId); private static native IBinder nativeCreateDisplay(String name, boolean secure); + private static native void nativeDestroyDisplay(IBinder displayToken); private static native void nativeSetDisplaySurface( IBinder displayToken, int nativeSurfaceObject); private static native void nativeSetDisplayLayerStack( IBinder displayToken, int layerStack); private static native void nativeSetDisplayProjection( IBinder displayToken, int orientation, - int l, int t, int r, int b, + int l, int t, int r, int b, int L, int T, int R, int B); private static native boolean nativeGetDisplayInfo( IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo); @@ -103,7 +104,7 @@ public class SurfaceControl { * measures will be taken to disallow the surface's content to be copied * from another process. In particular, screenshots and VNC servers will * be disabled, but other measures can take place, for instance the - * surface might not be hardware accelerated. + * surface might not be hardware accelerated. * */ public static final int SECURE = 0x00000080; @@ -247,10 +248,10 @@ public class SurfaceControl { throw new OutOfResourcesException( "Couldn't allocate SurfaceControl native object"); } - + mCloseGuard.open("release"); } - + @Override protected void finalize() throws Throwable { try { @@ -300,7 +301,7 @@ public class SurfaceControl { if (mNativeObject == 0) throw new NullPointerException( "mNativeObject is null. Have you called release() already?"); } - + /* * set surface parameters. * needs to be inside open/closeTransaction block @@ -369,7 +370,7 @@ public class SurfaceControl { public void setWindowCrop(Rect crop) { checkNotReleased(); if (crop != null) { - nativeSetWindowCrop(mNativeObject, + nativeSetWindowCrop(mNativeObject, crop.left, crop.top, crop.right, crop.bottom); } else { nativeSetWindowCrop(mNativeObject, 0, 0, 0, 0); @@ -397,19 +398,19 @@ public class SurfaceControl { public float xDpi; public float yDpi; public boolean secure; - + public PhysicalDisplayInfo() { } - + public PhysicalDisplayInfo(PhysicalDisplayInfo other) { copyFrom(other); } - + @Override public boolean equals(Object o) { return o instanceof PhysicalDisplayInfo && equals((PhysicalDisplayInfo)o); } - + public boolean equals(PhysicalDisplayInfo other) { return other != null && width == other.width @@ -420,12 +421,12 @@ public class SurfaceControl { && yDpi == other.yDpi && secure == other.secure; } - + @Override public int hashCode() { return 0; // don't care } - + public void copyFrom(PhysicalDisplayInfo other) { width = other.width; height = other.height; @@ -435,7 +436,7 @@ public class SurfaceControl { yDpi = other.yDpi; secure = other.secure; } - + // For debugging purposes @Override public String toString() { @@ -481,7 +482,7 @@ public class SurfaceControl { throw new IllegalArgumentException("displayRect must not be null"); } nativeSetDisplayProjection(displayToken, orientation, - layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom, + layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom, displayRect.left, displayRect.top, displayRect.right, displayRect.bottom); } @@ -513,6 +514,13 @@ public class SurfaceControl { return nativeCreateDisplay(name, secure); } + public static void destroyDisplay(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + nativeDestroyDisplay(displayToken); + } + public static IBinder getBuiltInDisplay(int builtInDisplayId) { return nativeGetBuiltInDisplay(builtInDisplayId); } @@ -608,7 +616,7 @@ public class SurfaceControl { SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); return nativeScreenshot(displayToken, width, height, 0, 0, true); } - + private static void screenshot(IBinder display, Surface consumer, int width, int height, int minLayer, int maxLayer, boolean allLayers) { if (display == null) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 20938f5..3dff1b0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -12062,7 +12062,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Resolve padding depending on layout direction. + * Resolves padding depending on layout direction, if applicable, and + * recomputes internal padding values to adjust for scroll bars. * * @hide */ @@ -12102,11 +12103,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom; - internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, - mUserPaddingBottom); onRtlPropertiesChanged(resolvedLayoutDirection); } + internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom); + mPrivateFlags2 |= PFLAG2_PADDING_RESOLVED; } @@ -14659,13 +14660,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ protected void resolveDrawables() { - if (canResolveLayoutDirection()) { - if (mBackground != null) { - mBackground.setLayoutDirection(getLayoutDirection()); - } - mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED; - onResolveDrawables(getLayoutDirection()); + // Drawables resolution may need to happen before resolving the layout direction (which is + // done only during the measure() call). + // If the layout direction is not resolved yet, we cannot resolve the Drawables except in + // one case: when the raw layout direction has not been defined as LAYOUT_DIRECTION_INHERIT. + // So, if the raw layout direction is LAYOUT_DIRECTION_LTR or LAYOUT_DIRECTION_RTL or + // LAYOUT_DIRECTION_LOCALE, we can "cheat" and we don't need to wait for the layout + // direction to be resolved as its resolved value will be the same as its raw value. + if (!isLayoutDirectionResolved() && + getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT) { + return; + } + + final int layoutDirection = isLayoutDirectionResolved() ? + getLayoutDirection() : getRawLayoutDirection(); + + if (mBackground != null) { + mBackground.setLayoutDirection(layoutDirection); } + mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED; + onResolveDrawables(layoutDirection); } /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 3f391ad..0ed846b 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1243,6 +1243,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mFastScroller = new FastScroller(this); mFastScroller.setEnabled(true); } + + recomputePadding(); + + if (mFastScroller != null) { + mFastScroller.updateLayout(); + } } /** @@ -1303,7 +1309,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @Override public int getVerticalScrollbarWidth() { - if (isFastScrollAlwaysVisible() && mFastScroller != null) { + if (mFastScroller != null && mFastScroller.isEnabled()) { return Math.max(super.getVerticalScrollbarWidth(), mFastScroller.getWidth()); } return super.getVerticalScrollbarWidth(); @@ -1327,6 +1333,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + @Override + public void setScrollBarStyle(int style) { + super.setScrollBarStyle(style); + if (mFastScroller != null) { + mFastScroller.setScrollBarStyle(style); + } + } + /** * If fast scroll is enabled, then don't draw the vertical scrollbar. * @hide @@ -2787,7 +2801,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @Override public void onRtlPropertiesChanged(int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); - if (mFastScroller != null) { mFastScroller.setScrollbarPosition(getVerticalScrollbarPosition()); } diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 393720f..c48955f 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -131,6 +131,9 @@ class FastScroller { /** Whether there is a track image to display. */ private final boolean mHasTrackImage; + /** Total width of decorations. */ + private final int mWidth; + /** Set containing decoration transition animations. */ private AnimatorSet mDecorAnimation; @@ -155,6 +158,9 @@ class FastScroller { /** The index of the current section. */ private int mCurrentSection = -1; + /** The current scrollbar position. */ + private int mScrollbarPosition = -1; + /** Whether the list is long enough to need a fast scroller. */ private boolean mLongList; @@ -194,6 +200,9 @@ class FastScroller { */ private int mOverlayPosition; + /** Current scrollbar style, including inset and overlay properties. */ + private int mScrollBarStyle; + /** Whether to precisely match the thumb position to the list. */ private boolean mMatchDragPosition; @@ -245,34 +254,44 @@ class FastScroller { final Resources res = context.getResources(); final TypedArray ta = context.getTheme().obtainStyledAttributes(ATTRS); - mTrackImage = new ImageView(context); + final ImageView trackImage = new ImageView(context); + mTrackImage = trackImage; + + int width = 0; // Add track to overlay if it has an image. - final int trackResId = ta.getResourceId(TRACK_DRAWABLE, 0); - if (trackResId != 0) { + final Drawable trackDrawable = ta.getDrawable(TRACK_DRAWABLE); + if (trackDrawable != null) { mHasTrackImage = true; - mTrackImage.setBackgroundResource(trackResId); - mOverlay.add(mTrackImage); + trackImage.setBackground(trackDrawable); + mOverlay.add(trackImage); + width = Math.max(width, trackDrawable.getIntrinsicWidth()); } else { mHasTrackImage = false; } - mThumbImage = new ImageView(context); + final ImageView thumbImage = new ImageView(context); + mThumbImage = thumbImage; // Add thumb to overlay if it has an image. final Drawable thumbDrawable = ta.getDrawable(THUMB_DRAWABLE); if (thumbDrawable != null) { - mThumbImage.setImageDrawable(thumbDrawable); - mOverlay.add(mThumbImage); + thumbImage.setImageDrawable(thumbDrawable); + mOverlay.add(thumbImage); + width = Math.max(width, thumbDrawable.getIntrinsicWidth()); } // If necessary, apply minimum thumb width and height. if (thumbDrawable.getIntrinsicWidth() <= 0 || thumbDrawable.getIntrinsicHeight() <= 0) { - mThumbImage.setMinimumWidth(res.getDimensionPixelSize(R.dimen.fastscroll_thumb_width)); - mThumbImage.setMinimumHeight( + final int minWidth = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_width); + thumbImage.setMinimumWidth(minWidth); + thumbImage.setMinimumHeight( res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height)); + width = Math.max(width, minWidth); } + mWidth = width; + final int previewSize = res.getDimensionPixelSize(R.dimen.fastscroll_overlay_size); mPreviewImage = new ImageView(context); mPreviewImage.setMinimumWidth(previewSize); @@ -297,10 +316,11 @@ class FastScroller { mOverlayPosition = ta.getInt(OVERLAY_POSITION, OVERLAY_FLOATING); ta.recycle(); + mScrollBarStyle = listView.getScrollBarStyle(); mScrollCompleted = true; mState = STATE_VISIBLE; - mMatchDragPosition = - context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB; + mMatchDragPosition = context.getApplicationInfo().targetSdkVersion + >= Build.VERSION_CODES.HONEYCOMB; getSectionsFromIndexer(); refreshDrawablePressedState(); @@ -362,6 +382,14 @@ class FastScroller { return mAlwaysShow; } + public void setScrollBarStyle(int style) { + if (mScrollBarStyle != style) { + mScrollBarStyle = style; + + updateLayout(); + } + } + /** * Immediately transitions the fast scroller decorations to a hidden state. */ @@ -375,25 +403,29 @@ class FastScroller { View.SCROLLBAR_POSITION_LEFT : View.SCROLLBAR_POSITION_RIGHT; } - mLayoutFromRight = position != View.SCROLLBAR_POSITION_LEFT; + if (mScrollbarPosition != position) { + mScrollbarPosition = position; + mLayoutFromRight = position != View.SCROLLBAR_POSITION_LEFT; - final int previewResId = mPreviewResId[mLayoutFromRight ? PREVIEW_RIGHT : PREVIEW_LEFT]; - mPreviewImage.setBackgroundResource(previewResId); + final int previewResId = mPreviewResId[mLayoutFromRight ? PREVIEW_RIGHT : PREVIEW_LEFT]; + mPreviewImage.setBackgroundResource(previewResId); - // Add extra padding for text. - final Drawable background = mPreviewImage.getBackground(); - if (background != null) { - final Rect padding = mTempBounds; - background.getPadding(padding); - padding.offset(mPreviewPadding, mPreviewPadding); - mPreviewImage.setPadding(padding.left, padding.top, padding.right, padding.bottom); - } + // Add extra padding for text. + final Drawable background = mPreviewImage.getBackground(); + if (background != null) { + final Rect padding = mTempBounds; + background.getPadding(padding); + padding.offset(mPreviewPadding, mPreviewPadding); + mPreviewImage.setPadding(padding.left, padding.top, padding.right, padding.bottom); + } - updateLayout(); + // Requires re-layout. + updateLayout(); + } } public int getWidth() { - return mThumbImage.getWidth(); + return mWidth; } public void onSizeChanged(int w, int h, int oldw, int oldh) { @@ -437,7 +469,7 @@ class FastScroller { /** * Measures and layouts the scrollbar and decorations. */ - private void updateLayout() { + public void updateLayout() { // Prevent re-entry when RTL properties change as a side-effect of // resolving padding. if (mUpdatingLayout) { @@ -594,21 +626,36 @@ class FastScroller { out.set(left, top, right, bottom); } + /** + * Updates the container rectangle used for layout. + */ private void updateContainerRect() { final AbsListView list = mList; + list.resolvePadding(); + final Rect container = mContainerRect; container.left = 0; container.top = 0; container.right = list.getWidth(); container.bottom = list.getHeight(); - final int scrollbarStyle = list.getScrollBarStyle(); + final int scrollbarStyle = mScrollBarStyle; if (scrollbarStyle == View.SCROLLBARS_INSIDE_INSET || scrollbarStyle == View.SCROLLBARS_INSIDE_OVERLAY) { container.left += list.getPaddingLeft(); container.top += list.getPaddingTop(); container.right -= list.getPaddingRight(); container.bottom -= list.getPaddingBottom(); + + // In inset mode, we need to adjust for padded scrollbar width. + if (scrollbarStyle == View.SCROLLBARS_INSIDE_INSET) { + final int width = getWidth(); + if (mScrollbarPosition == View.SCROLLBAR_POSITION_RIGHT) { + container.right += width; + } else { + container.left -= width; + } + } } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3181164..9c21f0d 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -1378,6 +1378,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else { dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0; } + resetResolvedDrawables(); + resolveDrawables(); } } diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index 3a2b647..91c47d1 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -18,91 +18,96 @@ package com.android.internal.app; import android.app.Activity; import android.content.ActivityNotFoundException; +import android.content.Context; import android.content.Intent; import android.graphics.Typeface; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.text.method.AllCapsTransformationMethod; +import android.text.method.TransformationMethod; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.view.animation.DecelerateInterpolator; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; public class PlatLogoActivity extends Activity { - Toast mToast; - ImageView mContent; + FrameLayout mContent; int mCount; final Handler mHandler = new Handler(); - private View makeView() { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); - LinearLayout view = new LinearLayout(this); - view.setOrientation(LinearLayout.VERTICAL); - view.setLayoutParams( - new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - )); - final int p = (int)(8 * metrics.density); - view.setPadding(p, p, p, p); - + Typeface bold = Typeface.create("sans-serif", Typeface.BOLD); Typeface light = Typeface.create("sans-serif-light", Typeface.NORMAL); - Typeface normal = Typeface.create("sans-serif", Typeface.BOLD); - final float size = 14 * metrics.density; - final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_HORIZONTAL; - lp.bottomMargin = (int) (-4*metrics.density); + mContent = new FrameLayout(this); + + final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER; - TextView tv = new TextView(this); + final ImageView logo = new ImageView(this); + logo.setImageResource(com.android.internal.R.drawable.platlogo); + logo.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + logo.setVisibility(View.INVISIBLE); + + final TextView letter = new TextView(this); + + letter.setTypeface(bold); + letter.setTextSize(300); + letter.setTextColor(0xFFFFFFFF); + letter.setGravity(Gravity.CENTER); + letter.setShadowLayer(12*metrics.density, 0, 0, 0xC085F985); + letter.setText(String.valueOf(Build.VERSION.RELEASE).substring(0, 1)); + + final int p = (int)(4 * metrics.density); + + final TextView tv = new TextView(this); if (light != null) tv.setTypeface(light); - tv.setTextSize(1.25f*size); + tv.setTextSize(30); + tv.setPadding(p, p, p, p); tv.setTextColor(0xFFFFFFFF); - tv.setShadowLayer(4*metrics.density, 0, 2*metrics.density, 0x66000000); + tv.setGravity(Gravity.CENTER); + tv.setShadowLayer(4 * metrics.density, 0, 2 * metrics.density, 0x66000000); + tv.setTransformationMethod(new AllCapsTransformationMethod(this)); tv.setText("Android " + Build.VERSION.RELEASE); - view.addView(tv, lp); - - tv = new TextView(this); - if (normal != null) tv.setTypeface(normal); - tv.setTextSize(size); - tv.setTextColor(0xFFFFFFFF); - tv.setShadowLayer(4*metrics.density, 0, 2*metrics.density, 0x66000000); - tv.setText("JELLY BEAN"); - view.addView(tv, lp); + tv.setVisibility(View.INVISIBLE); - return view; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + mContent.addView(letter, lp); + mContent.addView(logo, lp); - mToast = Toast.makeText(this, "", Toast.LENGTH_LONG); - mToast.setView(makeView()); + final FrameLayout.LayoutParams lp2 = new FrameLayout.LayoutParams(lp); + lp2.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + lp2.bottomMargin = 10*p; - DisplayMetrics metrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(metrics); - - mContent = new ImageView(this); - mContent.setImageResource(com.android.internal.R.drawable.platlogo_alt); - mContent.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - - final int p = (int)(32 * metrics.density); - mContent.setPadding(p, p, p, p); + mContent.addView(tv, lp2); mContent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mToast.show(); - mContent.setImageResource(com.android.internal.R.drawable.platlogo); + if (logo.getVisibility() != View.VISIBLE) { + letter.animate().alpha(0.25f).scaleY(0.75f).scaleX(0.75f).setDuration(2000) + .start(); + logo.setAlpha(0f); + logo.setVisibility(View.VISIBLE); + logo.animate().alpha(1f).setDuration(1000).setStartDelay(500).start(); + tv.setAlpha(0f); + tv.setVisibility(View.VISIBLE); + tv.animate().alpha(1f).setDuration(1000).setStartDelay(1000).start(); + } } }); @@ -115,9 +120,8 @@ public class PlatLogoActivity extends Activity { | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) .addCategory("com.android.internal.category.PLATLOGO")); - //.setClassName("com.android.systemui","com.android.systemui.BeanBag")); } catch (ActivityNotFoundException ex) { - android.util.Log.e("PlatLogoActivity", "Couldn't find a bag of beans."); + android.util.Log.e("PlatLogoActivity", "Couldn't find a piece of pie."); } finish(); return true; diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java new file mode 100644 index 0000000..6ff0304 --- /dev/null +++ b/core/java/com/android/internal/app/ProcessMap.java @@ -0,0 +1,55 @@ +/* + * 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.internal.app; + +import android.util.ArrayMap; +import android.util.SparseArray; + +public class ProcessMap<E> { + final ArrayMap<String, SparseArray<E>> mMap + = new ArrayMap<String, SparseArray<E>>(); + + public E get(String name, int uid) { + SparseArray<E> uids = mMap.get(name); + if (uids == null) return null; + return uids.get(uid); + } + + public E put(String name, int uid, E value) { + SparseArray<E> uids = mMap.get(name); + if (uids == null) { + uids = new SparseArray<E>(2); + mMap.put(name, uids); + } + uids.put(uid, value); + return value; + } + + public void remove(String name, int uid) { + SparseArray<E> uids = mMap.get(name); + if (uids != null) { + uids.remove(uid); + if (uids.size() == 0) { + mMap.remove(name); + } + } + } + + public ArrayMap<String, SparseArray<E>> getMap() { + return mMap; + } +} diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java new file mode 100644 index 0000000..7eadbb5 --- /dev/null +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -0,0 +1,2660 @@ +/* + * Copyright (C) 2013 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.internal.app; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.text.format.DateFormat; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; +import android.webkit.WebViewFactory; +import com.android.internal.util.ArrayUtils; +import dalvik.system.VMRuntime; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; + +public final class ProcessStats implements Parcelable { + static final String TAG = "ProcessStats"; + static final boolean DEBUG = false; + + public static final String SERVICE_NAME = "procstats"; + + public static final int STATE_NOTHING = -1; + public static final int STATE_PERSISTENT = 0; + public static final int STATE_TOP = 1; + public static final int STATE_IMPORTANT_FOREGROUND = 2; + public static final int STATE_IMPORTANT_BACKGROUND = 3; + public static final int STATE_BACKUP = 4; + public static final int STATE_HEAVY_WEIGHT = 5; + public static final int STATE_SERVICE = 6; + public static final int STATE_SERVICE_RESTARTING = 7; + public static final int STATE_RECEIVER = 8; + public static final int STATE_HOME = 9; + public static final int STATE_LAST_ACTIVITY = 10; + public static final int STATE_CACHED_ACTIVITY = 11; + public static final int STATE_CACHED_ACTIVITY_CLIENT = 12; + public static final int STATE_CACHED_EMPTY = 13; + public static final int STATE_COUNT = STATE_CACHED_EMPTY+1; + + public static final int PSS_SAMPLE_COUNT = 0; + public static final int PSS_MINIMUM = 1; + public static final int PSS_AVERAGE = 2; + public static final int PSS_MAXIMUM = 3; + public static final int PSS_USS_MINIMUM = 4; + public static final int PSS_USS_AVERAGE = 5; + public static final int PSS_USS_MAXIMUM = 6; + public static final int PSS_COUNT = PSS_USS_MAXIMUM+1; + + public static final int ADJ_NOTHING = -1; + public static final int ADJ_MEM_FACTOR_NORMAL = 0; + public static final int ADJ_MEM_FACTOR_MODERATE = 1; + public static final int ADJ_MEM_FACTOR_LOW = 2; + public static final int ADJ_MEM_FACTOR_CRITICAL = 3; + public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1; + public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT; + public static final int ADJ_SCREEN_OFF = 0; + public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD; + public static final int ADJ_COUNT = ADJ_SCREEN_ON*2; + + public static final int FLAG_COMPLETE = 1<<0; + public static final int FLAG_SHUTDOWN = 1<<1; + public static final int FLAG_SYSPROPS = 1<<2; + + public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, + ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL }; + + public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON }; + + public static final int[] NON_CACHED_PROC_STATES = new int[] { + STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND, + STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, + STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER + }; + + public static final int[] BACKGROUND_PROC_STATES = new int[] { + STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, + STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER + }; + + // Map from process states to the states we track. + static final int[] PROCESS_STATE_TO_STATE = new int[] { + STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT + STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI + STATE_TOP, // ActivityManager.PROCESS_STATE_TOP + STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND + STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP + STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT + STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE + STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER + STATE_HOME, // ActivityManager.PROCESS_STATE_HOME + STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY + STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY + STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT + STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY + }; + + public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, + STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, + STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, + STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY, + STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY + }; + + static final String[] STATE_NAMES = new String[] { + "Persistent", "Top ", "Imp Fg ", "Imp Bg ", + "Backup ", "Heavy Wght", "Service ", "Service Rs", + "Receiver ", "Home ", + "Last Act ", "Cch Act ", "Cch CliAct", "Cch Empty " + }; + + public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] { + "off", "on" + }; + + public static final String[] ADJ_MEM_NAMES_CSV = new String[] { + "norm", "mod", "low", "crit" + }; + + public static final String[] STATE_NAMES_CSV = new String[] { + "pers", "top", "impfg", "impbg", "backup", "heavy", + "service", "service-rs", "receiver", "home", "lastact", + "cch-activity", "cch-aclient", "cch-empty" + }; + + static final String[] ADJ_SCREEN_TAGS = new String[] { + "0", "1" + }; + + static final String[] ADJ_MEM_TAGS = new String[] { + "n", "m", "l", "c" + }; + + static final String[] STATE_TAGS = new String[] { + "p", "t", "f", "b", "u", "w", + "s", "x", "r", "h", "l", "a", "c", "e" + }; + + static final String CSV_SEP = "\t"; + + // Current version of the parcel format. + private static final int PARCEL_VERSION = 9; + // In-memory Parcel magic number, used to detect attempts to unmarshall bad data + private static final int MAGIC = 0x50535453; + + // Where the "type"/"state" part of the data appears in an offset integer. + static int OFFSET_TYPE_SHIFT = 0; + static int OFFSET_TYPE_MASK = 0xff; + // Where the "which array" part of the data appears in an offset integer. + static int OFFSET_ARRAY_SHIFT = 8; + static int OFFSET_ARRAY_MASK = 0xff; + // Where the "index into array" part of the data appears in an offset integer. + static int OFFSET_INDEX_SHIFT = 16; + static int OFFSET_INDEX_MASK = 0xffff; + + public String mReadError; + public String mTimePeriodStartClockStr; + public int mFlags; + + public final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>(); + public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>(); + + public final long[] mMemFactorDurations = new long[ADJ_COUNT]; + public int mMemFactor = STATE_NOTHING; + public long mStartTime; + + public long mTimePeriodStartClock; + public long mTimePeriodStartRealtime; + public long mTimePeriodEndRealtime; + String mRuntime; + String mWebView; + boolean mRunning; + + static final int LONGS_SIZE = 4096; + final ArrayList<long[]> mLongs = new ArrayList<long[]>(); + int mNextLong; + + int[] mAddLongTable; + int mAddLongTableSize; + + public ProcessStats(boolean running) { + mRunning = running; + reset(); + } + + public ProcessStats(Parcel in) { + reset(); + readFromParcel(in); + } + + public void add(ProcessStats other) { + ArrayMap<String, SparseArray<PackageState>> pkgMap = other.mPackages.getMap(); + for (int ip=0; ip<pkgMap.size(); ip++) { + String pkgName = pkgMap.keyAt(ip); + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + PackageState otherState = uids.valueAt(iu); + final int NPROCS = otherState.mProcesses.size(); + final int NSRVS = otherState.mServices.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState otherProc = otherState.mProcesses.valueAt(iproc); + if (otherProc.mCommonProcess != otherProc) { + if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid + + " proc " + otherProc.mName); + ProcessState thisProc = getProcessStateLocked(pkgName, uid, + otherProc.mName); + if (thisProc.mCommonProcess == thisProc) { + if (DEBUG) Slog.d(TAG, "Existing process is single-package, splitting"); + thisProc.mMultiPackage = true; + long now = SystemClock.uptimeMillis(); + final PackageState pkgState = getPackageStateLocked(pkgName, uid); + thisProc = thisProc.clone(thisProc.mPackage, now); + pkgState.mProcesses.put(thisProc.mName, thisProc); + } + thisProc.add(otherProc); + } + } + for (int isvc=0; isvc<NSRVS; isvc++) { + ServiceState otherSvc = otherState.mServices.valueAt(isvc); + if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid + + " service " + otherSvc.mName); + ServiceState thisSvc = getServiceStateLocked(pkgName, uid, + null, otherSvc.mName); + thisSvc.add(otherSvc); + } + } + } + + ArrayMap<String, SparseArray<ProcessState>> procMap = other.mProcesses.getMap(); + for (int ip=0; ip<procMap.size(); ip++) { + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + ProcessState otherProc = uids.valueAt(iu); + ProcessState thisProc = mProcesses.get(otherProc.mName, uid); + if (DEBUG) Slog.d(TAG, "Adding uid " + uid + " proc " + otherProc.mName); + if (thisProc == null) { + if (DEBUG) Slog.d(TAG, "Creating new process!"); + thisProc = new ProcessState(this, otherProc.mPackage, uid, otherProc.mName); + mProcesses.put(otherProc.mName, uid, thisProc); + PackageState thisState = getPackageStateLocked(otherProc.mPackage, uid); + if (!thisState.mProcesses.containsKey(otherProc.mName)) { + thisState.mProcesses.put(otherProc.mName, thisProc); + } + } + thisProc.add(otherProc); + } + } + + for (int i=0; i<ADJ_COUNT; i++) { + if (DEBUG) Slog.d(TAG, "Total duration #" + i + " inc by " + + other.mMemFactorDurations[i] + " from " + + mMemFactorDurations[i]); + mMemFactorDurations[i] += other.mMemFactorDurations[i]; + } + + if (other.mTimePeriodStartClock < mTimePeriodStartClock) { + mTimePeriodStartClock = other.mTimePeriodStartClock; + mTimePeriodStartClockStr = other.mTimePeriodStartClockStr; + } + mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime; + } + + public static final Parcelable.Creator<ProcessStats> CREATOR + = new Parcelable.Creator<ProcessStats>() { + public ProcessStats createFromParcel(Parcel in) { + return new ProcessStats(in); + } + + public ProcessStats[] newArray(int size) { + return new ProcessStats[size]; + } + }; + + private static void printScreenLabel(PrintWriter pw, int offset) { + switch (offset) { + case ADJ_NOTHING: + pw.print(" "); + break; + case ADJ_SCREEN_OFF: + pw.print("Screen Off / "); + break; + case ADJ_SCREEN_ON: + pw.print("Screen On / "); + break; + default: + pw.print("?????????? / "); + break; + } + } + + public static void printScreenLabelCsv(PrintWriter pw, int offset) { + switch (offset) { + case ADJ_NOTHING: + break; + case ADJ_SCREEN_OFF: + pw.print(ADJ_SCREEN_NAMES_CSV[0]); + break; + case ADJ_SCREEN_ON: + pw.print(ADJ_SCREEN_NAMES_CSV[1]); + break; + default: + pw.print("???"); + break; + } + } + + private static void printMemLabel(PrintWriter pw, int offset) { + switch (offset) { + case ADJ_NOTHING: + pw.print(" "); + break; + case ADJ_MEM_FACTOR_NORMAL: + pw.print("Norm / "); + break; + case ADJ_MEM_FACTOR_MODERATE: + pw.print("Mod / "); + break; + case ADJ_MEM_FACTOR_LOW: + pw.print("Low / "); + break; + case ADJ_MEM_FACTOR_CRITICAL: + pw.print("Crit / "); + break; + default: + pw.print("???? / "); + break; + } + } + + public static void printMemLabelCsv(PrintWriter pw, int offset) { + if (offset >= ADJ_MEM_FACTOR_NORMAL) { + if (offset <= ADJ_MEM_FACTOR_CRITICAL) { + pw.print(ADJ_MEM_NAMES_CSV[offset]); + } else { + pw.print("???"); + } + } + } + + public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations, + int curState, long curStartTime, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { + int printedMem = -1; + for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { + int state = imem+iscreen; + long time = durations[state]; + String running = ""; + if (curState == state) { + time += now - curStartTime; + if (pw != null) { + running = " (running)"; + } + } + if (time != 0) { + if (pw != null) { + pw.print(prefix); + printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printedMem = imem; + TimeUtils.formatDuration(time, pw); pw.println(running); + } + totalTime += time; + } + } + } + if (totalTime != 0 && pw != null) { + pw.print(prefix); + printScreenLabel(pw, STATE_NOTHING); + pw.print("TOTAL: "); + TimeUtils.formatDuration(totalTime, pw); + pw.println(); + } + return totalTime; + } + + static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations, + int curState, long curStartTime, long now) { + for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { + for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { + int state = imem+iscreen; + long time = durations[state]; + if (curState == state) { + time += now - curStartTime; + } + if (time != 0) { + printAdjTagAndValue(pw, state, time); + } + } + } + } + + static void dumpServiceTimeCheckin(PrintWriter pw, String label, String packageName, + int uid, String serviceName, ServiceState svc, int serviceType, int opCount, + int curState, long curStartTime, long now) { + if (opCount <= 0) { + return; + } + pw.print(label); + pw.print(","); + pw.print(packageName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(serviceName); + pw.print(","); + pw.print(opCount); + boolean didCurState = false; + for (int i=0; i<svc.mDurationsTableSize; i++) { + int off = svc.mDurationsTable[i]; + int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + int memFactor = type / ServiceState.SERVICE_COUNT; + type %= ServiceState.SERVICE_COUNT; + if (type != serviceType) { + continue; + } + long time = svc.mStats.getLong(off, 0); + if (curState == memFactor) { + didCurState = true; + time += now - curStartTime; + } + printAdjTagAndValue(pw, memFactor, time); + } + if (!didCurState && curState != STATE_NOTHING) { + printAdjTagAndValue(pw, curState, now - curStartTime); + } + pw.println(); + } + + public static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) { + data.totalTime = 0; + data.numPss = data.minPss = data.avgPss = data.maxPss = + data.minUss = data.avgUss = data.maxUss = 0; + for (int is=0; is<data.screenStates.length; is++) { + for (int im=0; im<data.memStates.length; im++) { + for (int ip=0; ip<data.procStates.length; ip++) { + int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT) + + data.procStates[ip]; + data.totalTime += proc.getDuration(bucket, now); + long samples = proc.getPssSampleCount(bucket); + if (samples > 0) { + long minPss = proc.getPssMinimum(bucket); + long avgPss = proc.getPssAverage(bucket); + long maxPss = proc.getPssMaximum(bucket); + long minUss = proc.getPssUssMinimum(bucket); + long avgUss = proc.getPssUssAverage(bucket); + long maxUss = proc.getPssUssMaximum(bucket); + if (data.numPss == 0) { + data.minPss = minPss; + data.avgPss = avgPss; + data.maxPss = maxPss; + data.minUss = minUss; + data.avgUss = avgUss; + data.maxUss = maxUss; + } else { + if (minPss < data.minPss) { + data.minPss = minPss; + } + data.avgPss = (long)( ((data.avgPss*(double)data.numPss) + + (avgPss*(double)samples)) / (data.numPss+samples) ); + if (maxPss > data.maxPss) { + data.maxPss = maxPss; + } + if (minUss < data.minUss) { + data.minUss = minUss; + } + data.avgUss = (long)( ((data.avgUss*(double)data.numPss) + + (avgUss*(double)samples)) / (data.numPss+samples) ); + if (maxUss > data.maxUss) { + data.maxUss = maxUss; + } + } + data.numPss += samples; + } + } + } + } + } + + static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates, + int[] procStates, long now) { + long totalTime = 0; + /* + for (int i=0; i<proc.mDurationsTableSize; i++) { + int val = proc.mDurationsTable[i]; + totalTime += proc.mState.getLong(val, 0); + if ((val&0xff) == proc.mCurState) { + totalTime += now - proc.mStartTime; + } + } + */ + for (int is=0; is<screenStates.length; is++) { + for (int im=0; im<memStates.length; im++) { + for (int ip=0; ip<procStates.length; ip++) { + int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT) + + procStates[ip]; + totalTime += proc.getDuration(bucket, now); + } + } + } + proc.mTmpTotalTime = totalTime; + return totalTime; + } + + static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc, + int[] screenStates, int[] memStates, int[] procStates, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int is=0; is<screenStates.length; is++) { + int printedMem = -1; + for (int im=0; im<memStates.length; im++) { + for (int ip=0; ip<procStates.length; ip++) { + final int iscreen = screenStates[is]; + final int imem = memStates[im]; + final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; + long time = proc.getDuration(bucket, now); + String running = ""; + if (proc.mCurState == bucket) { + running = " (running)"; + } + if (time != 0) { + pw.print(prefix); + if (screenStates.length > 1) { + printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + } + if (memStates.length > 1) { + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printedMem = imem; + } + pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); + TimeUtils.formatDuration(time, pw); pw.println(running); + totalTime += time; + } + } + } + } + if (totalTime != 0) { + pw.print(prefix); + if (screenStates.length > 1) { + printScreenLabel(pw, STATE_NOTHING); + } + if (memStates.length > 1) { + printMemLabel(pw, STATE_NOTHING); + } + pw.print("TOTAL : "); + TimeUtils.formatDuration(totalTime, pw); + pw.println(); + } + } + + static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates, + int[] memStates, int[] procStates) { + boolean printedHeader = false; + int printedScreen = -1; + for (int is=0; is<screenStates.length; is++) { + int printedMem = -1; + for (int im=0; im<memStates.length; im++) { + for (int ip=0; ip<procStates.length; ip++) { + final int iscreen = screenStates[is]; + final int imem = memStates[im]; + final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; + long count = proc.getPssSampleCount(bucket); + if (count > 0) { + if (!printedHeader) { + pw.print(prefix); + pw.print("PSS/USS ("); + pw.print(proc.mPssTableSize); + pw.println(" entries):"); + printedHeader = true; + } + pw.print(prefix); + pw.print(" "); + if (screenStates.length > 1) { + printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + } + if (memStates.length > 1) { + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printedMem = imem; + } + pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); + pw.print(count); + pw.print(" samples "); + printSizeValue(pw, proc.getPssMinimum(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssAverage(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssMaximum(bucket) * 1024); + pw.print(" / "); + printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024); + pw.print(" "); + printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024); + pw.println(); + } + } + } + } + if (proc.mNumExcessiveWake != 0) { + pw.print(prefix); pw.print("Killed for excessive wake locks: "); + pw.print(proc.mNumExcessiveWake); pw.println(" times"); + } + if (proc.mNumExcessiveCpu != 0) { + pw.print(prefix); pw.print("Killed for excessive CPU use: "); + pw.print(proc.mNumExcessiveCpu); pw.println(" times"); + } + } + + static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates, + int[] memStates, int[] procStates) { + final int NS = screenStates != null ? screenStates.length : 1; + final int NM = memStates != null ? memStates.length : 1; + final int NP = procStates != null ? procStates.length : 1; + for (int is=0; is<NS; is++) { + for (int im=0; im<NM; im++) { + for (int ip=0; ip<NP; ip++) { + pw.print(sep); + boolean printed = false; + if (screenStates != null && screenStates.length > 1) { + printScreenLabelCsv(pw, screenStates[is]); + printed = true; + } + if (memStates != null && memStates.length > 1) { + if (printed) { + pw.print("-"); + } + printMemLabelCsv(pw, memStates[im]); + printed = true; + } + if (procStates != null && procStates.length > 1) { + if (printed) { + pw.print("-"); + } + pw.print(STATE_NAMES_CSV[procStates[ip]]); + } + } + } + } + } + + static void dumpProcessStateCsv(PrintWriter pw, ProcessState proc, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, + boolean sepProcStates, int[] procStates, long now) { + final int NSS = sepScreenStates ? screenStates.length : 1; + final int NMS = sepMemStates ? memStates.length : 1; + final int NPS = sepProcStates ? procStates.length : 1; + for (int iss=0; iss<NSS; iss++) { + for (int ims=0; ims<NMS; ims++) { + for (int ips=0; ips<NPS; ips++) { + final int vsscreen = sepScreenStates ? screenStates[iss] : 0; + final int vsmem = sepMemStates ? memStates[ims] : 0; + final int vsproc = sepProcStates ? procStates[ips] : 0; + final int NSA = sepScreenStates ? 1 : screenStates.length; + final int NMA = sepMemStates ? 1 : memStates.length; + final int NPA = sepProcStates ? 1 : procStates.length; + long totalTime = 0; + for (int isa=0; isa<NSA; isa++) { + for (int ima=0; ima<NMA; ima++) { + for (int ipa=0; ipa<NPA; ipa++) { + final int vascreen = sepScreenStates ? 0 : screenStates[isa]; + final int vamem = sepMemStates ? 0 : memStates[ima]; + final int vaproc = sepProcStates ? 0 : procStates[ipa]; + final int bucket = ((vsscreen + vascreen + vsmem + vamem) + * STATE_COUNT) + vsproc + vaproc; + totalTime += proc.getDuration(bucket, now); + } + } + } + pw.print(CSV_SEP); + pw.print(totalTime); + } + } + } + } + + static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs, + int[] screenStates, int[] memStates, int[] procStates, long now) { + String innerPrefix = prefix + " "; + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(prefix); + pw.print(proc.mName); + pw.print(" / "); + UserHandle.formatUid(pw, proc.mUid); + pw.print(" ("); + pw.print(proc.mDurationsTableSize); + pw.print(" entries)"); + pw.println(":"); + dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now); + if (proc.mPssTableSize > 0) { + dumpProcessPss(pw, innerPrefix, proc, screenStates, memStates, procStates); + } + } + } + + static void dumpProcessSummaryDetails(PrintWriter pw, ProcessState proc, String prefix, + String label, int[] screenStates, int[] memStates, int[] procStates, + long now, long totalTime, boolean full) { + ProcessDataCollection totals = new ProcessDataCollection(screenStates, + memStates, procStates); + computeProcessData(proc, totals, now); + if (totals.totalTime != 0 || totals.numPss != 0) { + if (prefix != null) { + pw.print(prefix); + } + if (label != null) { + pw.print(label); + } + totals.print(pw, totalTime, full); + if (prefix != null) { + pw.println(); + } + } + } + + static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, + ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates, + long now, long totalTime) { + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(prefix); + pw.print("* "); + pw.print(proc.mName); + pw.print(" / "); + UserHandle.formatUid(pw, proc.mUid); + pw.println(":"); + dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates, + procStates, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates, + new int[] { STATE_PERSISTENT }, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates, + new int[] {STATE_TOP}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Imp Fg: ", screenStates, memStates, + new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Imp Bg: ", screenStates, memStates, + new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates, + new int[] {STATE_BACKUP}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Heavy Wgt: ", screenStates, memStates, + new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates, + new int[] {STATE_SERVICE}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Service Rs: ", screenStates, memStates, + new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Receiver: ", screenStates, memStates, + new int[] {STATE_RECEIVER}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " Home: ", screenStates, memStates, + new int[] {STATE_HOME}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " (Last Act): ", screenStates, memStates, + new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true); + dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates, + new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT, + STATE_CACHED_EMPTY}, now, totalTime, true); + } + } + + static void printPercent(PrintWriter pw, double fraction) { + fraction *= 100; + if (fraction < 1) { + pw.print(String.format("%.2f", fraction)); + } else if (fraction < 10) { + pw.print(String.format("%.1f", fraction)); + } else { + pw.print(String.format("%.0f", fraction)); + } + pw.print("%"); + } + + static void printSizeValue(PrintWriter pw, long number) { + float result = number; + String suffix = ""; + if (result > 900) { + suffix = "KB"; + result = result / 1024; + } + if (result > 900) { + suffix = "MB"; + result = result / 1024; + } + if (result > 900) { + suffix = "GB"; + result = result / 1024; + } + if (result > 900) { + suffix = "TB"; + result = result / 1024; + } + if (result > 900) { + suffix = "PB"; + result = result / 1024; + } + String value; + if (result < 1) { + value = String.format("%.2f", result); + } else if (result < 10) { + value = String.format("%.1f", result); + } else if (result < 100) { + value = String.format("%.0f", result); + } else { + value = String.format("%.0f", result); + } + pw.print(value); + pw.print(suffix); + } + + public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, + boolean sepProcStates, int[] procStates, long now) { + pw.print("process"); + pw.print(CSV_SEP); + pw.print("uid"); + dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null, + sepMemStates ? memStates : null, + sepProcStates ? procStates : null); + pw.println(); + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(proc.mName); + pw.print(CSV_SEP); + UserHandle.formatUid(pw, proc.mUid); + dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates, + sepMemStates, memStates, sepProcStates, procStates, now); + pw.println(); + } + } + + static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) { + int index = value/mod; + if (index >= 0 && index < array.length) { + pw.print(array[index]); + } else { + pw.print('?'); + } + return value - index*mod; + } + + static void printProcStateTag(PrintWriter pw, int state) { + state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT); + state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT); + printArrayEntry(pw, STATE_TAGS, state, 1); + } + + static void printAdjTag(PrintWriter pw, int state) { + state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD); + printArrayEntry(pw, ADJ_MEM_TAGS, state, 1); + } + + static void printProcStateTagAndValue(PrintWriter pw, int state, long value) { + pw.print(','); + printProcStateTag(pw, state); + pw.print(':'); + pw.print(value); + } + + static void printAdjTagAndValue(PrintWriter pw, int state, long value) { + pw.print(','); + printAdjTag(pw, state); + pw.print(':'); + pw.print(value); + } + + static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) { + boolean didCurState = false; + for (int i=0; i<proc.mDurationsTableSize; i++) { + int off = proc.mDurationsTable[i]; + int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + long time = proc.mStats.getLong(off, 0); + if (proc.mCurState == type) { + didCurState = true; + time += now - proc.mStartTime; + } + printProcStateTagAndValue(pw, type, time); + } + if (!didCurState && proc.mCurState != STATE_NOTHING) { + printProcStateTagAndValue(pw, proc.mCurState, now - proc.mStartTime); + } + } + + static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) { + for (int i=0; i<proc.mPssTableSize; i++) { + int off = proc.mPssTable[i]; + int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + long count = proc.mStats.getLong(off, PSS_SAMPLE_COUNT); + long min = proc.mStats.getLong(off, PSS_MINIMUM); + long avg = proc.mStats.getLong(off, PSS_AVERAGE); + long max = proc.mStats.getLong(off, PSS_MAXIMUM); + long umin = proc.mStats.getLong(off, PSS_USS_MINIMUM); + long uavg = proc.mStats.getLong(off, PSS_USS_AVERAGE); + long umax = proc.mStats.getLong(off, PSS_USS_MAXIMUM); + pw.print(','); + printProcStateTag(pw, type); + pw.print(':'); + pw.print(count); + pw.print(':'); + pw.print(min); + pw.print(':'); + pw.print(avg); + pw.print(':'); + pw.print(max); + pw.print(':'); + pw.print(umin); + pw.print(':'); + pw.print(uavg); + pw.print(':'); + pw.print(umax); + } + } + + public void reset() { + if (DEBUG) Slog.d(TAG, "Resetting state of " + mTimePeriodStartClockStr); + resetCommon(); + mPackages.getMap().clear(); + mProcesses.getMap().clear(); + mMemFactor = STATE_NOTHING; + mStartTime = 0; + if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); + } + + public void resetSafely() { + if (DEBUG) Slog.d(TAG, "Safely resetting state of " + mTimePeriodStartClockStr); + resetCommon(); + long now = SystemClock.uptimeMillis(); + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int ip=procMap.size()-1; ip>=0; ip--) { + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=uids.size()-1; iu>=0; iu--) { + uids.valueAt(iu).resetSafely(now); + } + } + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + for (int ip=pkgMap.size()-1; ip>=0; ip--) { + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + for (int iu=uids.size()-1; iu>=0; iu--) { + PackageState pkgState = uids.valueAt(iu); + for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) { + pkgState.mProcesses.valueAt(iproc).resetSafely(now); + } + for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) { + ServiceState ss = pkgState.mServices.valueAt(isvc); + if (ss.isActive()) { + pkgState.mServices.valueAt(isvc).resetSafely(now); + } else { + pkgState.mServices.removeAt(isvc); + } + } + } + } + mStartTime = SystemClock.uptimeMillis(); + if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); + } + + private void resetCommon() { + mTimePeriodStartClock = System.currentTimeMillis(); + buildTimePeriodStartClockStr(); + mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); + mLongs.clear(); + mLongs.add(new long[LONGS_SIZE]); + mNextLong = 0; + Arrays.fill(mMemFactorDurations, 0); + mStartTime = 0; + mReadError = null; + mFlags = 0; + evaluateSystemProperties(true); + } + + public boolean evaluateSystemProperties(boolean update) { + boolean changed = false; + String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib", + VMRuntime.getRuntime().vmLibrary()); + if (!Objects.equals(runtime, mRuntime)) { + changed = true; + if (update) { + mRuntime = runtime; + } + } + String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview"; + if (!Objects.equals(webview, mWebView)) { + changed = true; + if (update) { + mWebView = webview; + } + } + return changed; + } + + private void buildTimePeriodStartClockStr() { + mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss", + mTimePeriodStartClock).toString(); + } + + static final int[] BAD_TABLE = new int[0]; + + private int[] readTableFromParcel(Parcel in, String name, String what) { + final int size = in.readInt(); + if (size < 0) { + Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size); + return BAD_TABLE; + } + if (size == 0) { + return null; + } + final int[] table = new int[size]; + for (int i=0; i<size; i++) { + table[i] = in.readInt(); + if (DEBUG) Slog.i(TAG, "Reading in " + name + " table #" + i + ": " + + ProcessStats.printLongOffset(table[i])); + if (!validateLongOffset(table[i])) { + Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: " + + ProcessStats.printLongOffset(table[i])); + return null; + } + } + return table; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + long now = SystemClock.uptimeMillis(); + out.writeInt(MAGIC); + out.writeInt(PARCEL_VERSION); + out.writeInt(STATE_COUNT); + out.writeInt(ADJ_COUNT); + out.writeInt(PSS_COUNT); + out.writeInt(LONGS_SIZE); + + // First commit all running times. + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + final int NPROC = procMap.size(); + for (int ip=0; ip<NPROC; ip++) { + SparseArray<ProcessState> uids = procMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + uids.valueAt(iu).commitStateTime(now); + } + } + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + final int NPKG = pkgMap.size(); + for (int ip=0; ip<NPKG; ip++) { + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (proc.mCommonProcess != proc) { + proc.commitStateTime(now); + } + } + final int NSRVS = pkgState.mServices.size(); + for (int isvc=0; isvc<NSRVS; isvc++) { + pkgState.mServices.valueAt(isvc).commitStateTime(now); + } + } + } + + out.writeLong(mTimePeriodStartClock); + out.writeLong(mTimePeriodStartRealtime); + out.writeLong(mTimePeriodEndRealtime); + out.writeString(mRuntime); + out.writeString(mWebView); + out.writeInt(mFlags); + + out.writeInt(mLongs.size()); + out.writeInt(mNextLong); + for (int i=0; i<(mLongs.size()-1); i++) { + out.writeLongArray(mLongs.get(i)); + } + long[] lastLongs = mLongs.get(mLongs.size() - 1); + for (int i=0; i<mNextLong; i++) { + out.writeLong(lastLongs[i]); + if (DEBUG) Slog.d(TAG, "Writing last long #" + i + ": " + lastLongs[i]); + } + + if (mMemFactor != STATE_NOTHING) { + mMemFactorDurations[mMemFactor] += now - mStartTime; + mStartTime = now; + } + out.writeLongArray(mMemFactorDurations); + + out.writeInt(NPROC); + for (int ip=0; ip<NPROC; ip++) { + out.writeString(procMap.keyAt(ip)); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + final int NUID = uids.size(); + out.writeInt(NUID); + for (int iu=0; iu<NUID; iu++) { + out.writeInt(uids.keyAt(iu)); + ProcessState proc = uids.valueAt(iu); + out.writeString(proc.mPackage); + proc.writeToParcel(out, now); + } + } + out.writeInt(NPKG); + for (int ip=0; ip<NPKG; ip++) { + out.writeString(pkgMap.keyAt(ip)); + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + final int NUID = uids.size(); + out.writeInt(NUID); + for (int iu=0; iu<NUID; iu++) { + out.writeInt(uids.keyAt(iu)); + PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + out.writeInt(NPROCS); + for (int iproc=0; iproc<NPROCS; iproc++) { + out.writeString(pkgState.mProcesses.keyAt(iproc)); + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (proc.mCommonProcess == proc) { + // This is the same as the common process we wrote above. + out.writeInt(0); + } else { + // There is separate data for this package's process. + out.writeInt(1); + proc.writeToParcel(out, now); + } + } + final int NSRVS = pkgState.mServices.size(); + out.writeInt(NSRVS); + for (int isvc=0; isvc<NSRVS; isvc++) { + out.writeString(pkgState.mServices.keyAt(isvc)); + ServiceState svc = pkgState.mServices.valueAt(isvc); + svc.writeToParcel(out, now); + } + } + } + } + + private boolean readCheckedInt(Parcel in, int val, String what) { + int got; + if ((got=in.readInt()) != val) { + mReadError = "bad " + what + ": " + got; + return false; + } + return true; + } + + static byte[] readFully(InputStream stream) throws 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; + } + } + } + + public void read(InputStream stream) { + try { + byte[] raw = readFully(stream); + Parcel in = Parcel.obtain(); + in.unmarshall(raw, 0, raw.length); + in.setDataPosition(0); + stream.close(); + + readFromParcel(in); + } catch (IOException e) { + mReadError = "caught exception: " + e; + } + } + + public void readFromParcel(Parcel in) { + final boolean hadData = mPackages.getMap().size() > 0 + || mProcesses.getMap().size() > 0; + if (hadData) { + resetSafely(); + } + + if (!readCheckedInt(in, MAGIC, "magic number")) { + return; + } + int version = in.readInt(); + if (version != PARCEL_VERSION && version != 6) { + mReadError = "bad version: " + version; + return; + } + if (!readCheckedInt(in, STATE_COUNT, "state count")) { + return; + } + if (!readCheckedInt(in, ADJ_COUNT, "adj count")) { + return; + } + if (!readCheckedInt(in, PSS_COUNT, "pss count")) { + return; + } + if (!readCheckedInt(in, LONGS_SIZE, "longs size")) { + return; + } + + mTimePeriodStartClock = in.readLong(); + buildTimePeriodStartClockStr(); + mTimePeriodStartRealtime = in.readLong(); + mTimePeriodEndRealtime = in.readLong(); + if (version == PARCEL_VERSION) { + mRuntime = in.readString(); + mWebView = in.readString(); + } + mFlags = in.readInt(); + + final int NLONGS = in.readInt(); + final int NEXTLONG = in.readInt(); + mLongs.clear(); + for (int i=0; i<(NLONGS-1); i++) { + while (i >= mLongs.size()) { + mLongs.add(new long[LONGS_SIZE]); + } + in.readLongArray(mLongs.get(i)); + } + long[] longs = new long[LONGS_SIZE]; + mNextLong = NEXTLONG; + for (int i=0; i<NEXTLONG; i++) { + longs[i] = in.readLong(); + if (DEBUG) Slog.d(TAG, "Reading last long #" + i + ": " + longs[i]); + } + mLongs.add(longs); + + in.readLongArray(mMemFactorDurations); + + int NPROC = in.readInt(); + if (NPROC < 0) { + mReadError = "bad process count: " + NPROC; + return; + } + while (NPROC > 0) { + NPROC--; + String procName = in.readString(); + if (procName == null) { + mReadError = "bad process name"; + return; + } + int NUID = in.readInt(); + if (NUID < 0) { + mReadError = "bad uid count: " + NUID; + return; + } + while (NUID > 0) { + NUID--; + int uid = in.readInt(); + if (uid < 0) { + mReadError = "bad uid: " + uid; + return; + } + String pkgName = in.readString(); + if (pkgName == null) { + mReadError = "bad process package name"; + return; + } + ProcessState proc = hadData ? mProcesses.get(procName, uid) : null; + if (proc != null) { + if (!proc.readFromParcel(in, false)) { + return; + } + } else { + proc = new ProcessState(this, pkgName, uid, procName); + if (!proc.readFromParcel(in, true)) { + return; + } + } + if (DEBUG) Slog.d(TAG, "Adding process: " + procName + " " + uid + " " + proc); + mProcesses.put(procName, uid, proc); + } + } + + if (DEBUG) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes"); + + int NPKG = in.readInt(); + if (NPKG < 0) { + mReadError = "bad package count: " + NPKG; + return; + } + while (NPKG > 0) { + NPKG--; + String pkgName = in.readString(); + if (pkgName == null) { + mReadError = "bad package name"; + return; + } + int NUID = in.readInt(); + if (NUID < 0) { + mReadError = "bad uid count: " + NUID; + return; + } + while (NUID > 0) { + NUID--; + int uid = in.readInt(); + if (uid < 0) { + mReadError = "bad uid: " + uid; + return; + } + PackageState pkgState = new PackageState(uid); + mPackages.put(pkgName, uid, pkgState); + int NPROCS = in.readInt(); + if (NPROCS < 0) { + mReadError = "bad package process count: " + NPROCS; + return; + } + while (NPROCS > 0) { + NPROCS--; + String procName = in.readString(); + if (procName == null) { + mReadError = "bad package process name"; + return; + } + int hasProc = in.readInt(); + if (DEBUG) Slog.d(TAG, "Reading package " + pkgName + " " + uid + + " process " + procName + " hasProc=" + hasProc); + ProcessState commonProc = mProcesses.get(procName, uid); + if (DEBUG) Slog.d(TAG, "Got common proc " + procName + " " + uid + + ": " + commonProc); + if (commonProc == null) { + mReadError = "no common proc: " + procName; + return; + } + if (hasProc != 0) { + // The process for this package is unique to the package; we + // need to load it. We don't need to do anything about it if + // it is not unique because if someone later looks for it + // they will find and use it from the global procs. + ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null; + if (proc != null) { + if (!proc.readFromParcel(in, false)) { + return; + } + } else { + proc = new ProcessState(commonProc, pkgName, uid, procName, 0); + if (!proc.readFromParcel(in, true)) { + return; + } + } + if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: " + + procName + " " + uid + " " + proc); + pkgState.mProcesses.put(procName, proc); + } else { + if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: " + + procName + " " + uid + " " + commonProc); + pkgState.mProcesses.put(procName, commonProc); + } + } + int NSRVS = in.readInt(); + if (NSRVS < 0) { + mReadError = "bad package service count: " + NSRVS; + return; + } + while (NSRVS > 0) { + NSRVS--; + String serviceName = in.readString(); + if (serviceName == null) { + mReadError = "bad package service name"; + return; + } + ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null; + if (serv == null) { + serv = new ServiceState(this, pkgName, serviceName, null); + } + if (!serv.readFromParcel(in)) { + return; + } + if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " service: " + + serviceName + " " + uid + " " + serv); + pkgState.mServices.put(serviceName, serv); + } + } + } + + if (DEBUG) Slog.d(TAG, "Successfully read procstats!"); + } + + int addLongData(int index, int type, int num) { + int tableLen = mAddLongTable != null ? mAddLongTable.length : 0; + if (mAddLongTableSize >= tableLen) { + int newSize = ArrayUtils.idealIntArraySize(tableLen + 1); + int[] newTable = new int[newSize]; + if (tableLen > 0) { + System.arraycopy(mAddLongTable, 0, newTable, 0, tableLen); + } + mAddLongTable = newTable; + } + if (mAddLongTableSize > 0 && mAddLongTableSize - index != 0) { + System.arraycopy(mAddLongTable, index, mAddLongTable, index + 1, + mAddLongTableSize - index); + } + int off = allocLongData(num); + mAddLongTable[index] = type | off; + mAddLongTableSize++; + return off; + } + + int allocLongData(int num) { + int whichLongs = mLongs.size()-1; + long[] longs = mLongs.get(whichLongs); + if (mNextLong + num > longs.length) { + longs = new long[LONGS_SIZE]; + mLongs.add(longs); + whichLongs++; + mNextLong = 0; + } + int off = (whichLongs<<OFFSET_ARRAY_SHIFT) | (mNextLong<<OFFSET_INDEX_SHIFT); + mNextLong += num; + return off; + } + + boolean validateLongOffset(int off) { + int arr = (off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK; + if (arr >= mLongs.size()) { + return false; + } + int idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK; + if (idx >= LONGS_SIZE) { + return false; + } + if (DEBUG) Slog.d(TAG, "Validated long " + printLongOffset(off) + + ": " + getLong(off, 0)); + return true; + } + + static String printLongOffset(int off) { + StringBuilder sb = new StringBuilder(16); + sb.append("a"); sb.append((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + sb.append("i"); sb.append((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK); + sb.append("t"); sb.append((off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK); + return sb.toString(); + } + + void setLong(int off, int index, long value) { + long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)] = value; + } + + long getLong(int off, int index) { + long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + return longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)]; + } + + static int binarySearch(int[] array, int size, int value) { + int lo = 0; + int hi = size - 1; + + while (lo <= hi) { + int mid = (lo + hi) >>> 1; + int midVal = (array[mid] >> OFFSET_TYPE_SHIFT) & OFFSET_TYPE_MASK; + + if (midVal < value) { + lo = mid + 1; + } else if (midVal > value) { + hi = mid - 1; + } else { + return mid; // value found + } + } + return ~lo; // value not present + } + + public PackageState getPackageStateLocked(String packageName, int uid) { + PackageState as = mPackages.get(packageName, uid); + if (as != null) { + return as; + } + as = new PackageState(uid); + mPackages.put(packageName, uid, as); + return as; + } + + public ProcessState getProcessStateLocked(String packageName, int uid, String processName) { + final PackageState pkgState = getPackageStateLocked(packageName, uid); + ProcessState ps = pkgState.mProcesses.get(processName); + if (ps != null) { + return ps; + } + ProcessState commonProc = mProcesses.get(processName, uid); + if (commonProc == null) { + commonProc = new ProcessState(this, packageName, uid, processName); + mProcesses.put(processName, uid, commonProc); + } + if (!commonProc.mMultiPackage) { + if (packageName.equals(commonProc.mPackage)) { + // This common process is not in use by multiple packages, and + // is for the calling package, so we can just use it directly. + ps = commonProc; + } else { + // This common process has not been in use by multiple packages, + // but it was created for a different package than the caller. + // We need to convert it to a multi-package process. + commonProc.mMultiPackage = true; + // The original package it was created for now needs to point + // to its own copy. + long now = SystemClock.uptimeMillis(); + pkgState.mProcesses.put(commonProc.mName, commonProc.clone( + commonProc.mPackage, now)); + ps = new ProcessState(commonProc, packageName, uid, processName, now); + } + } else { + // The common process is for multiple packages, we need to create a + // separate object for the per-package data. + ps = new ProcessState(commonProc, packageName, uid, processName, + SystemClock.uptimeMillis()); + } + pkgState.mProcesses.put(processName, ps); + return ps; + } + + public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid, + String processName, String className) { + final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid); + ProcessStats.ServiceState ss = as.mServices.get(className); + if (ss != null) { + ss.makeActive(); + return ss; + } + final ProcessStats.ProcessState ps = processName != null + ? getProcessStateLocked(packageName, uid, processName) : null; + ss = new ProcessStats.ServiceState(this, packageName, className, ps); + as.mServices.put(className, ss); + return ss; + } + + public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpAll) { + long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, + mStartTime, now); + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + boolean printedHeader = false; + for (int ip=0; ip<pkgMap.size(); ip++) { + String pkgName = pkgMap.keyAt(ip); + if (reqPackage != null && !reqPackage.equals(pkgName)) { + continue; + } + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + final int NSRVS = pkgState.mServices.size(); + if (NPROCS > 0 || NSRVS > 0) { + if (!printedHeader) { + pw.println("Per-Package Process Stats:"); + printedHeader = true; + } + pw.print(" * "); pw.print(pkgName); pw.print(" / "); + UserHandle.formatUid(pw, uid); pw.println(":"); + } + if (dumpAll) { + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + pw.print(" Process "); + pw.print(pkgState.mProcesses.keyAt(iproc)); + pw.print(" ("); + pw.print(proc.mDurationsTableSize); + pw.print(" entries)"); + pw.println(":"); + dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES); + if (dumpAll) { + pw.print(" mNumStartedServices="); + pw.println(proc.mNumStartedServices); + } + } + } else { + ArrayList<ProcessState> procs = new ArrayList<ProcessState>(); + for (int iproc=0; iproc<NPROCS; iproc++) { + procs.add(pkgState.mProcesses.valueAt(iproc)); + } + dumpProcessSummaryLocked(pw, " ", procs, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + NON_CACHED_PROC_STATES, now, totalTime); + } + for (int isvc=0; isvc<NSRVS; isvc++) { + if (dumpAll) { + pw.print(" Service "); + } else { + pw.print(" * "); + } + pw.print(pkgState.mServices.keyAt(isvc)); + pw.println(":"); + ServiceState svc = pkgState.mServices.valueAt(isvc); + dumpServiceStats(pw, " ", " ", " ", "Started", svc, + svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState, + svc.mStartedStartTime, now, totalTime, dumpAll); + dumpServiceStats(pw, " ", " ", " ", "Bound", svc, + svc.mBoundCount, ServiceState.SERVICE_BOUND, svc.mBoundState, + svc.mBoundStartTime, now, totalTime, dumpAll); + dumpServiceStats(pw, " ", " ", " ", "Executing", svc, + svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState, + svc.mExecStartTime, now, totalTime, dumpAll); + } + } + } + + if (reqPackage == null) { + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + printedHeader = false; + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + ProcessState proc = uids.valueAt(iu); + if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING + && proc.mPssTableSize == 0) { + continue; + } + if (!printedHeader) { + pw.println("Process Stats:"); + printedHeader = true; + } + pw.print(" * "); pw.print(procName); pw.print(" / "); + UserHandle.formatUid(pw, uid); + pw.print(" ("); pw.print(proc.mDurationsTableSize); + pw.print(" entries)"); pw.println(":"); + dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES); + } + } + + pw.println(); + pw.println("Summary:"); + dumpSummaryLocked(pw, reqPackage, now); + } else { + pw.println(); + dumpTotalsLocked(pw, now); + } + + if (dumpAll) { + pw.println(); + pw.println("Internal state:"); + pw.print(" Num long arrays: "); pw.println(mLongs.size()); + pw.print(" Next long entry: "); pw.println(mNextLong); + pw.print(" mRunning="); pw.println(mRunning); + } + } + + static long dumpSingleServiceTime(PrintWriter pw, String prefix, ServiceState service, + int serviceType, int curState, long curStartTime, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { + int printedMem = -1; + for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { + int state = imem+iscreen; + long time = service.getDuration(serviceType, curState, curStartTime, + state, now); + String running = ""; + if (curState == state && pw != null) { + running = " (running)"; + } + if (time != 0) { + if (pw != null) { + pw.print(prefix); + printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING); + printedMem = imem; + TimeUtils.formatDuration(time, pw); pw.println(running); + } + totalTime += time; + } + } + } + if (totalTime != 0 && pw != null) { + pw.print(prefix); + printScreenLabel(pw, STATE_NOTHING); + pw.print("TOTAL: "); + TimeUtils.formatDuration(totalTime, pw); + pw.println(); + } + return totalTime; + } + + void dumpServiceStats(PrintWriter pw, String prefix, String prefixInner, + String headerPrefix, String header, ServiceState service, + int count, int serviceType, int state, long startTime, long now, long totalTime, + boolean dumpAll) { + if (count != 0) { + if (dumpAll) { + pw.print(prefix); pw.print(header); + pw.print(" op count "); pw.print(count); pw.println(":"); + dumpSingleServiceTime(pw, prefixInner, service, serviceType, state, startTime, + now); + } else { + long myTime = dumpSingleServiceTime(null, null, service, serviceType, state, + startTime, now); + pw.print(prefix); pw.print(headerPrefix); pw.print(header); + pw.print(" count "); pw.print(count); + pw.print(" / time "); + printPercent(pw, (double)myTime/(double)totalTime); + pw.println(); + } + } + } + + public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) { + long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, + mStartTime, now); + dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, + NON_CACHED_PROC_STATES, now, totalTime, reqPackage); + pw.println(); + dumpTotalsLocked(pw, now); + } + + void dumpTotalsLocked(PrintWriter pw, long now) { + pw.println("Run time Stats:"); + dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now); + pw.println(); + pw.print(" Start time: "); + pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock)); + pw.println(); + pw.print(" Total elapsed time: "); + TimeUtils.formatDuration( + (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime) + - mTimePeriodStartRealtime, pw); + boolean partial = true; + if ((mFlags&FLAG_SHUTDOWN) != 0) { + pw.print(" (shutdown)"); + partial = false; + } + if ((mFlags&FLAG_SYSPROPS) != 0) { + pw.print(" (sysprops)"); + partial = false; + } + if ((mFlags&FLAG_COMPLETE) != 0) { + pw.print(" (complete)"); + partial = false; + } + if (partial) { + pw.print(" (partial)"); + } + pw.print(' '); + pw.print(mRuntime); + pw.print(' '); + pw.print(mWebView); + pw.println(); + } + + void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, + int[] screenStates, int[] memStates, int[] procStates, long now, long totalTime, + String reqPackage) { + ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates, + procStates, now, reqPackage); + if (procs.size() > 0) { + if (header != null) { + pw.println(); + pw.println(header); + } + dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, procStates, + now, totalTime); + } + } + + public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates, + int[] procStates, long now, String reqPackage) { + ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>(); + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + for (int ip=0; ip<pkgMap.size(); ip++) { + if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) { + continue; + } + SparseArray<PackageState> procs = pkgMap.valueAt(ip); + for (int iu=0; iu<procs.size(); iu++) { + PackageState state = procs.valueAt(iu); + for (int iproc=0; iproc<state.mProcesses.size(); iproc++) { + ProcessState proc = state.mProcesses.valueAt(iproc); + foundProcs.add(proc.mCommonProcess); + } + } + } + ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size()); + for (int i=0; i<foundProcs.size(); i++) { + ProcessState proc = foundProcs.valueAt(i); + if (computeProcessTimeLocked(proc, screenStates, memStates, + procStates, now) > 0) { + outProcs.add(proc); + } + } + Collections.sort(outProcs, new Comparator<ProcessState>() { + @Override + public int compare(ProcessState lhs, ProcessState rhs) { + if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) { + return -1; + } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) { + return 1; + } + return 0; + } + }); + return outProcs; + } + + String collapseString(String pkgName, String itemName) { + if (itemName.startsWith(pkgName)) { + final int ITEMLEN = itemName.length(); + final int PKGLEN = pkgName.length(); + if (ITEMLEN == PKGLEN) { + return ""; + } else if (ITEMLEN >= PKGLEN) { + if (itemName.charAt(PKGLEN) == '.') { + return itemName.substring(PKGLEN); + } + } + } + return itemName; + } + + public void dumpCheckinLocked(PrintWriter pw, String reqPackage) { + final long now = SystemClock.uptimeMillis(); + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + pw.println("vers,3"); + pw.print("period,"); pw.print(mTimePeriodStartClockStr); + pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(","); + pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime); + boolean partial = true; + if ((mFlags&FLAG_SHUTDOWN) != 0) { + pw.print(",shutdown"); + partial = false; + } + if ((mFlags&FLAG_SYSPROPS) != 0) { + pw.print(",sysprops"); + partial = false; + } + if ((mFlags&FLAG_COMPLETE) != 0) { + pw.print(",complete"); + partial = false; + } + if (partial) { + pw.print(",partial"); + } + pw.println(); + pw.print("config,"); pw.print(mRuntime); pw.print(','); pw.println(mWebView); + for (int ip=0; ip<pkgMap.size(); ip++) { + String pkgName = pkgMap.keyAt(ip); + if (reqPackage != null && !reqPackage.equals(pkgName)) { + continue; + } + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + final int NSRVS = pkgState.mServices.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + pw.print("pkgproc,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); + dumpAllProcessStateCheckin(pw, proc, now); + pw.println(); + if (proc.mPssTableSize > 0) { + pw.print("pkgpss,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); + dumpAllProcessPssCheckin(pw, proc); + pw.println(); + } + if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) { + pw.print("pkgkills,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); + pw.print(","); + pw.print(proc.mNumExcessiveWake); + pw.print(","); + pw.print(proc.mNumExcessiveCpu); + pw.println(); + } + } + for (int isvc=0; isvc<NSRVS; isvc++) { + String serviceName = collapseString(pkgName, + pkgState.mServices.keyAt(isvc)); + ServiceState svc = pkgState.mServices.valueAt(isvc); + dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName, + svc, ServiceState.SERVICE_STARTED, svc.mStartedCount, + svc.mStartedState, svc.mStartedStartTime, now); + dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, serviceName, + svc, ServiceState.SERVICE_BOUND, svc.mBoundCount, + svc.mBoundState, svc.mBoundStartTime, now); + dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, serviceName, + svc, ServiceState.SERVICE_EXEC, svc.mExecCount, + svc.mExecState, svc.mExecStartTime, now); + } + } + } + + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + ProcessState procState = uids.valueAt(iu); + if (procState.mDurationsTableSize > 0) { + pw.print("proc,"); + pw.print(procName); + pw.print(","); + pw.print(uid); + dumpAllProcessStateCheckin(pw, procState, now); + pw.println(); + } + if (procState.mPssTableSize > 0) { + pw.print("pss,"); + pw.print(procName); + pw.print(","); + pw.print(uid); + dumpAllProcessPssCheckin(pw, procState); + pw.println(); + } + if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0) { + pw.print("kills,"); + pw.print(procName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(procState.mNumExcessiveWake); + pw.print(","); + pw.print(procState.mNumExcessiveCpu); + pw.println(); + } + } + } + pw.print("total"); + dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor, + mStartTime, now); + pw.println(); + } + + public static final class ProcessState { + public final ProcessStats mStats; + public final ProcessState mCommonProcess; + public final String mPackage; + public final int mUid; + public final String mName; + + int[] mDurationsTable; + int mDurationsTableSize; + + //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT]; + int mCurState = STATE_NOTHING; + long mStartTime; + + int mLastPssState = STATE_NOTHING; + long mLastPssTime; + int[] mPssTable; + int mPssTableSize; + + int mNumStartedServices; + + int mNumExcessiveWake; + int mNumExcessiveCpu; + + boolean mMultiPackage; + + public long mTmpTotalTime; + + /** + * Create a new top-level process state, for the initial case where there is only + * a single package running in a process. The initial state is not running. + */ + public ProcessState(ProcessStats processStats, String pkg, int uid, String name) { + mStats = processStats; + mCommonProcess = this; + mPackage = pkg; + mUid = uid; + mName = name; + } + + /** + * Create a new per-package process state for an existing top-level process + * state. The current running state of the top-level process is also copied, + * marked as started running at 'now'. + */ + public ProcessState(ProcessState commonProcess, String pkg, int uid, String name, + long now) { + mStats = commonProcess.mStats; + mCommonProcess = commonProcess; + mPackage = pkg; + mUid = uid; + mName = name; + mCurState = commonProcess.mCurState; + mStartTime = now; + } + + ProcessState clone(String pkg, long now) { + ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now); + if (mDurationsTable != null) { + mStats.mAddLongTable = new int[mDurationsTable.length]; + mStats.mAddLongTableSize = 0; + for (int i=0; i<mDurationsTableSize; i++) { + int origEnt = mDurationsTable[i]; + int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + int newOff = mStats.addLongData(i, type, 1); + mStats.mAddLongTable[i] = newOff | type; + mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0)); + } + pnew.mDurationsTable = mStats.mAddLongTable; + pnew.mDurationsTableSize = mStats.mAddLongTableSize; + } + if (mPssTable != null) { + mStats.mAddLongTable = new int[mPssTable.length]; + mStats.mAddLongTableSize = 0; + for (int i=0; i<mPssTableSize; i++) { + int origEnt = mPssTable[i]; + int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + int newOff = mStats.addLongData(i, type, PSS_COUNT); + mStats.mAddLongTable[i] = newOff | type; + for (int j=0; j<PSS_COUNT; j++) { + mStats.setLong(newOff, j, mStats.getLong(origEnt, j)); + } + } + pnew.mPssTable = mStats.mAddLongTable; + pnew.mPssTableSize = mStats.mAddLongTableSize; + } + pnew.mNumExcessiveWake = mNumExcessiveWake; + pnew.mNumExcessiveCpu = mNumExcessiveCpu; + pnew.mNumStartedServices = mNumStartedServices; + return pnew; + } + + void add(ProcessState other) { + for (int i=0; i<other.mDurationsTableSize; i++) { + int ent = other.mDurationsTable[i]; + int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + if (DEBUG) Slog.d(TAG, "Adding state " + state + " duration " + + other.mStats.getLong(ent, 0)); + addDuration(state, other.mStats.getLong(ent, 0)); + } + for (int i=0; i<other.mPssTableSize; i++) { + int ent = other.mPssTable[i]; + int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + addPss(state, (int) other.mStats.getLong(ent, PSS_SAMPLE_COUNT), + other.mStats.getLong(ent, PSS_MINIMUM), + other.mStats.getLong(ent, PSS_AVERAGE), + other.mStats.getLong(ent, PSS_MAXIMUM), + other.mStats.getLong(ent, PSS_USS_MINIMUM), + other.mStats.getLong(ent, PSS_USS_AVERAGE), + other.mStats.getLong(ent, PSS_USS_MAXIMUM)); + } + mNumExcessiveWake += other.mNumExcessiveWake; + mNumExcessiveCpu += other.mNumExcessiveCpu; + } + + void resetSafely(long now) { + mDurationsTable = null; + mDurationsTableSize = 0; + mStartTime = now; + mLastPssState = STATE_NOTHING; + mLastPssTime = 0; + mPssTable = null; + mPssTableSize = 0; + mNumExcessiveWake = 0; + mNumExcessiveCpu = 0; + } + + void writeToParcel(Parcel out, long now) { + out.writeInt(mMultiPackage ? 1 : 0); + out.writeInt(mDurationsTableSize); + for (int i=0; i<mDurationsTableSize; i++) { + if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": " + + printLongOffset(mDurationsTable[i])); + out.writeInt(mDurationsTable[i]); + } + out.writeInt(mPssTableSize); + for (int i=0; i<mPssTableSize; i++) { + if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": " + + printLongOffset(mPssTable[i])); + out.writeInt(mPssTable[i]); + } + out.writeInt(mNumExcessiveWake); + out.writeInt(mNumExcessiveCpu); + } + + boolean readFromParcel(Parcel in, boolean fully) { + boolean multiPackage = in.readInt() != 0; + if (fully) { + mMultiPackage = multiPackage; + } + if (DEBUG) Slog.d(TAG, "Reading durations table..."); + mDurationsTable = mStats.readTableFromParcel(in, mName, "durations"); + if (mDurationsTable == BAD_TABLE) { + return false; + } + mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0; + if (DEBUG) Slog.d(TAG, "Reading pss table..."); + mPssTable = mStats.readTableFromParcel(in, mName, "pss"); + if (mPssTable == BAD_TABLE) { + return false; + } + mPssTableSize = mPssTable != null ? mPssTable.length : 0; + mNumExcessiveWake = in.readInt(); + mNumExcessiveCpu = in.readInt(); + return true; + } + + /** + * Update the current state of the given list of processes. + * + * @param state Current ActivityManager.PROCESS_STATE_* + * @param memFactor Current mem factor constant. + * @param now Current time. + * @param pkgList Processes to update. + */ + public void setState(int state, int memFactor, long now, + ArrayMap<String, ProcessState> pkgList) { + if (state < 0) { + state = mNumStartedServices > 0 + ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING; + } else { + state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT); + } + + // First update the common process. + mCommonProcess.setState(state, now); + + // If the common process is not multi-package, there is nothing else to do. + if (!mCommonProcess.mMultiPackage) { + return; + } + + if (pkgList != null) { + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).setState(state, now); + } + } + } + + void setState(int state, long now) { + if (mCurState != state) { + //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state); + commitStateTime(now); + mCurState = state; + } + } + + void commitStateTime(long now) { + if (mCurState != STATE_NOTHING) { + long dur = now - mStartTime; + if (dur > 0) { + addDuration(mCurState, dur); + } + } + mStartTime = now; + } + + void addDuration(int state, long dur) { + int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); + int off; + if (idx >= 0) { + off = mDurationsTable[idx]; + } else { + mStats.mAddLongTable = mDurationsTable; + mStats.mAddLongTableSize = mDurationsTableSize; + off = mStats.addLongData(~idx, state, 1); + mDurationsTable = mStats.mAddLongTable; + mDurationsTableSize = mStats.mAddLongTableSize; + } + long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur + + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]); + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur; + } + + void incStartedServices(int memFactor, long now) { + if (mCommonProcess != this) { + mCommonProcess.incStartedServices(memFactor, now); + } + mNumStartedServices++; + if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) { + setState(STATE_NOTHING, memFactor, now, null); + } + } + + void decStartedServices(int memFactor, long now) { + if (mCommonProcess != this) { + mCommonProcess.decStartedServices(memFactor, now); + } + mNumStartedServices--; + if (mNumStartedServices == 0 && mCurState == STATE_SERVICE_RESTARTING) { + setState(STATE_NOTHING, memFactor, now, null); + } else if (mNumStartedServices < 0) { + throw new IllegalStateException("Proc started services underrun: pkg=" + + mPackage + " uid=" + mUid + " name=" + mName); + } + } + + public void addPss(long pss, long uss, boolean always) { + if (!always) { + if (mLastPssState == mCurState && SystemClock.uptimeMillis() + < (mLastPssTime+(30*1000))) { + return; + } + } + mLastPssState = mCurState; + mLastPssTime = SystemClock.uptimeMillis(); + if (mCurState != STATE_NOTHING) { + addPss(mCurState, 1, pss, pss, pss, uss, uss, uss); + } + } + + void addPss(int state, int inCount, long minPss, long avgPss, long maxPss, long minUss, + long avgUss, long maxUss) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + int off; + if (idx >= 0) { + off = mPssTable[idx]; + } else { + mStats.mAddLongTable = mPssTable; + mStats.mAddLongTableSize = mPssTableSize; + off = mStats.addLongData(~idx, state, PSS_COUNT); + mPssTable = mStats.mAddLongTable; + mPssTableSize = mStats.mAddLongTableSize; + } + long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK; + long count = longs[idx+PSS_SAMPLE_COUNT]; + if (count == 0) { + longs[idx+PSS_SAMPLE_COUNT] = inCount; + longs[idx+PSS_MINIMUM] = minPss; + longs[idx+PSS_AVERAGE] = avgPss; + longs[idx+PSS_MAXIMUM] = maxPss; + longs[idx+PSS_USS_MINIMUM] = minUss; + longs[idx+PSS_USS_AVERAGE] = avgUss; + longs[idx+PSS_USS_MAXIMUM] = maxUss; + } else { + longs[idx+PSS_SAMPLE_COUNT] = count+inCount; + if (longs[idx+PSS_MINIMUM] > minPss) { + longs[idx+PSS_MINIMUM] = minPss; + } + longs[idx+PSS_AVERAGE] = (long)( + ((longs[idx+PSS_AVERAGE]*(double)count)+(avgPss*(double)inCount)) + / (count+inCount) ); + if (longs[idx+PSS_MAXIMUM] < maxPss) { + longs[idx+PSS_MAXIMUM] = maxPss; + } + if (longs[idx+PSS_USS_MINIMUM] > minUss) { + longs[idx+PSS_USS_MINIMUM] = minUss; + } + longs[idx+PSS_USS_AVERAGE] = (long)( + ((longs[idx+PSS_USS_AVERAGE]*(double)count)+(avgUss*(double)inCount)) + / (count+inCount) ); + if (longs[idx+PSS_USS_MAXIMUM] < maxUss) { + longs[idx+PSS_USS_MAXIMUM] = maxUss; + } + } + } + + public void reportExcessiveWake(ArrayMap<String, ProcessState> pkgList) { + mCommonProcess.mNumExcessiveWake++; + if (!mCommonProcess.mMultiPackage) { + return; + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).mNumExcessiveWake++; + } + } + + public void reportExcessiveCpu(ArrayMap<String, ProcessState> pkgList) { + mCommonProcess.mNumExcessiveCpu++; + if (!mCommonProcess.mMultiPackage) { + return; + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).mNumExcessiveCpu++; + } + } + + ProcessState pullFixedProc(String pkgName) { + if (mMultiPackage) { + // The array map is still pointing to a common process state + // that is now shared across packages. Update it to point to + // the new per-package state. + ProcessState proc = mStats.mPackages.get(pkgName, + mUid).mProcesses.get(mName); + if (proc == null) { + throw new IllegalStateException("Didn't create per-package process"); + } + return proc; + } + return this; + } + + private ProcessState pullFixedProc(ArrayMap<String, ProcessState> pkgList, + int index) { + ProcessState proc = pkgList.valueAt(index); + if (proc.mMultiPackage) { + // The array map is still pointing to a common process state + // that is now shared across packages. Update it to point to + // the new per-package state. + proc = mStats.mPackages.get(pkgList.keyAt(index), + proc.mUid).mProcesses.get(proc.mName); + if (proc == null) { + throw new IllegalStateException("Didn't create per-package process"); + } + pkgList.setValueAt(index, proc); + } + return proc; + } + + long getDuration(int state, long now) { + int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); + long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0; + if (mCurState == state) { + time += now - mStartTime; + } + return time; + } + + long getPssSampleCount(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0; + } + + long getPssMinimum(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MINIMUM) : 0; + } + + long getPssAverage(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_AVERAGE) : 0; + } + + long getPssMaximum(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MAXIMUM) : 0; + } + + long getPssUssMinimum(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0; + } + + long getPssUssAverage(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0; + } + + long getPssUssMaximum(int state) { + int idx = binarySearch(mPssTable, mPssTableSize, state); + return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0; + } + } + + public static final class ServiceState { + final ProcessStats mStats; + final String mPackage; + final String mName; + ProcessState mProc; + + int mActive = 1; + + static final int SERVICE_STARTED = 0; + static final int SERVICE_BOUND = 1; + static final int SERVICE_EXEC = 2; + static final int SERVICE_COUNT = 3; + + int[] mDurationsTable; + int mDurationsTableSize; + + int mStartedCount; + public int mStartedState = STATE_NOTHING; + long mStartedStartTime; + + int mBoundCount; + public int mBoundState = STATE_NOTHING; + long mBoundStartTime; + + int mExecCount; + public int mExecState = STATE_NOTHING; + long mExecStartTime; + + public ServiceState(ProcessStats processStats, String pkg, String name, ProcessState proc) { + mStats = processStats; + mPackage = pkg; + mName = name; + mProc = proc; + } + + public void makeActive() { + mActive++; + } + + public void makeInactive() { + /* + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.i(TAG, "Making " + this + " inactive", here); + */ + mActive--; + } + + public boolean isActive() { + return mActive > 0; + } + + void add(ServiceState other) { + for (int i=0; i<other.mDurationsTableSize; i++) { + int ent = other.mDurationsTable[i]; + int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + addStateTime(state, other.mStats.getLong(ent, 0)); + } + mStartedCount += other.mStartedCount; + mBoundCount += other.mBoundCount; + mExecCount += other.mExecCount; + } + + void resetSafely(long now) { + mDurationsTable = null; + mDurationsTableSize = 0; + mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0; + mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0; + mExecCount = mExecState != STATE_NOTHING ? 1 : 0; + mStartedStartTime = mBoundStartTime = mExecStartTime = now; + } + + void writeToParcel(Parcel out, long now) { + out.writeInt(mDurationsTableSize); + for (int i=0; i<mDurationsTableSize; i++) { + if (DEBUG) Slog.i(TAG, "Writing service in " + mPackage + " dur #" + i + ": " + + printLongOffset(mDurationsTable[i])); + out.writeInt(mDurationsTable[i]); + } + out.writeInt(mStartedCount); + out.writeInt(mBoundCount); + out.writeInt(mExecCount); + } + + boolean readFromParcel(Parcel in) { + if (DEBUG) Slog.d(TAG, "Reading durations table..."); + mDurationsTable = mStats.readTableFromParcel(in, mPackage, "service"); + if (mDurationsTable == BAD_TABLE) { + return false; + } + mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0; + mStartedCount = in.readInt(); + mBoundCount = in.readInt(); + mExecCount = in.readInt(); + return true; + } + + void addStateTime(int state, long time) { + if (time > 0) { + int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); + int off; + if (idx >= 0) { + off = mDurationsTable[idx]; + } else { + mStats.mAddLongTable = mDurationsTable; + mStats.mAddLongTableSize = mDurationsTableSize; + off = mStats.addLongData(~idx, state, 1); + mDurationsTable = mStats.mAddLongTable; + mDurationsTableSize = mStats.mAddLongTableSize; + } + long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time; + } + } + + void commitStateTime(long now) { + if (mStartedState != STATE_NOTHING) { + addStateTime(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), + now - mStartedStartTime); + mStartedStartTime = now; + } + if (mBoundState != STATE_NOTHING) { + addStateTime(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime); + mBoundStartTime = now; + } + if (mExecState != STATE_NOTHING) { + addStateTime(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime); + mExecStartTime = now; + } + } + + public void setStarted(boolean started, int memFactor, long now) { + if (mActive <= 0) { + throw new IllegalStateException("Service " + this + " has mActive=" + mActive); + } + int state = started ? memFactor : STATE_NOTHING; + if (mStartedState != state) { + if (mStartedState != STATE_NOTHING) { + addStateTime(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), + now - mStartedStartTime); + } else if (started) { + mStartedCount++; + } + mStartedState = state; + mStartedStartTime = now; + if (mProc != null) { + mProc = mProc.pullFixedProc(mPackage); + if (started) { + mProc.incStartedServices(memFactor, now); + } else { + mProc.decStartedServices(memFactor, now); + } + } + } + } + + public void setBound(boolean bound, int memFactor, long now) { + if (mActive <= 0) { + throw new IllegalStateException("Service " + this + " has mActive=" + mActive); + } + int state = bound ? memFactor : STATE_NOTHING; + if (mBoundState != state) { + if (mBoundState != STATE_NOTHING) { + addStateTime(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), + now - mBoundStartTime); + } else if (bound) { + mBoundCount++; + } + mBoundState = state; + mBoundStartTime = now; + } + } + + public void setExecuting(boolean executing, int memFactor, long now) { + if (mActive <= 0) { + throw new IllegalStateException("Service " + this + " has mActive=" + mActive); + } + int state = executing ? memFactor : STATE_NOTHING; + if (mExecState != state) { + if (mExecState != STATE_NOTHING) { + addStateTime(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime); + } else if (executing) { + mExecCount++; + } + mExecState = state; + mExecStartTime = now; + } + } + + private long getDuration(int opType, int curState, long startTime, int memFactor, + long now) { + int state = opType + (memFactor*SERVICE_COUNT); + int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); + long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0; + if (curState == memFactor) { + time += now - startTime; + } + return time; + } + } + + public static final class PackageState { + public final ArrayMap<String, ProcessState> mProcesses + = new ArrayMap<String, ProcessState>(); + public final ArrayMap<String, ServiceState> mServices + = new ArrayMap<String, ServiceState>(); + final int mUid; + + public PackageState(int uid) { + mUid = uid; + } + } + + public static final class ProcessDataCollection { + final int[] screenStates; + final int[] memStates; + final int[] procStates; + + public long totalTime; + public long numPss; + public long minPss; + public long avgPss; + public long maxPss; + public long minUss; + public long avgUss; + public long maxUss; + + public ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) { + screenStates = _screenStates; + memStates = _memStates; + procStates = _procStates; + } + + void print(PrintWriter pw, long overallTime, boolean full) { + printPercent(pw, (double) totalTime / (double) overallTime); + if (numPss > 0) { + pw.print(" ("); + printSizeValue(pw, minPss * 1024); + pw.print("-"); + printSizeValue(pw, avgPss * 1024); + pw.print("-"); + printSizeValue(pw, maxPss * 1024); + pw.print("/"); + printSizeValue(pw, minUss * 1024); + pw.print("-"); + printSizeValue(pw, avgUss * 1024); + pw.print("-"); + printSizeValue(pw, maxUss * 1024); + if (full) { + pw.print(" over "); + pw.print(numPss); + } + pw.print(")"); + } + } + } +} diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ccc0089..04351da 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -499,7 +499,7 @@ public class ZygoteInit { String args[] = { "--setuid=1000", "--setgid=1000", - "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003,3006,3007", + "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007", "--capabilities=" + capabilities + "," + capabilities, "--runtime-init", "--nice-name=system_server", |
