summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/ActivityManager.java8
-rw-r--r--core/java/android/app/ContextImpl.java2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java31
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/bluetooth/BluetoothTetheringDataTracker.java5
-rw-r--r--core/java/android/content/res/Configuration.java11
-rwxr-xr-x[-rw-r--r--]core/java/android/database/DefaultDatabaseErrorHandler.java2
-rw-r--r--core/java/android/hardware/location/IFusedLocationHardware.aidl117
-rw-r--r--core/java/android/hardware/location/IFusedLocationHardwareSink.aidl41
-rw-r--r--core/java/android/net/BaseNetworkStateTracker.java5
-rw-r--r--core/java/android/net/CaptivePortalTracker.java44
-rw-r--r--core/java/android/net/ConnectivityManager.java33
-rw-r--r--core/java/android/net/DummyDataStateTracker.java6
-rw-r--r--core/java/android/net/EthernetDataTracker.java5
-rw-r--r--core/java/android/net/IConnectivityManager.aidl4
-rw-r--r--core/java/android/net/MobileDataStateTracker.java12
-rw-r--r--core/java/android/net/NetworkStateTracker.java5
-rw-r--r--core/java/android/os/BatteryStats.java3
-rw-r--r--core/java/android/os/FileUtils.java86
-rw-r--r--core/java/android/os/Process.java13
-rw-r--r--core/java/android/provider/Settings.java62
-rw-r--r--core/java/android/text/TextUtils.java8
-rw-r--r--core/java/android/view/SurfaceControl.java38
-rw-r--r--core/java/android/view/View.java32
-rw-r--r--core/java/android/widget/AbsListView.java17
-rw-r--r--core/java/android/widget/FastScroller.java101
-rw-r--r--core/java/android/widget/TextView.java2
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java110
-rw-r--r--core/java/com/android/internal/app/ProcessMap.java55
-rw-r--r--core/java/com/android/internal/app/ProcessStats.java2660
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java2
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",