Merge \\"Implement dumpsys --checkin for shortcut manager\\" into nyc-mr1-dev am: 121480cda8

am: 3991a376e5

Change-Id: I3ccc92a0a9f6bfcce5207ef29db73063ec50a9f8
This commit is contained in:
Makoto Onuki
2016-07-18 18:10:41 +00:00
committed by android-build-merger
8 changed files with 346 additions and 18 deletions

View File

@@ -25,6 +25,8 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.ShortcutUser.PackageWithUser;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -288,6 +290,15 @@ class ShortcutLauncher extends ShortcutPackageItem {
}
}
@Override
public JSONObject dumpCheckin(boolean clear) throws JSONException {
final JSONObject result = super.dumpCheckin(clear);
// Nothing really interesting to dump.
return result;
}
@VisibleForTesting
ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) {
return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)));

View File

@@ -36,6 +36,8 @@ import com.android.internal.util.XmlUtils;
import com.android.server.pm.ShortcutService.ShortcutOperation;
import com.android.server.pm.ShortcutService.Stats;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -93,6 +95,12 @@ class ShortcutPackage extends ShortcutPackageItem {
private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array";
private static final String ATTR_NAME_XMLUTILS = "name";
private static final String KEY_DYNAMIC = "dynamic";
private static final String KEY_MANIFEST = "manifest";
private static final String KEY_PINNED = "pinned";
private static final String KEY_BITMAPS = "bitmaps";
private static final String KEY_BITMAP_BYTES = "bitmapBytes";
/**
* All the shortcuts from the package, keyed on IDs.
*/
@@ -1198,6 +1206,42 @@ class ShortcutPackage extends ShortcutPackageItem {
pw.println(")");
}
@Override
public JSONObject dumpCheckin(boolean clear) throws JSONException {
final JSONObject result = super.dumpCheckin(clear);
int numDynamic = 0;
int numPinned = 0;
int numManifest = 0;
int numBitmaps = 0;
long totalBitmapSize = 0;
final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
final int size = shortcuts.size();
for (int i = 0; i < size; i++) {
final ShortcutInfo si = shortcuts.valueAt(i);
if (si.isDynamic()) numDynamic++;
if (si.isDeclaredInManifest()) numManifest++;
if (si.isPinned()) numPinned++;
if (si.getBitmapPath() != null) {
numBitmaps++;
totalBitmapSize += new File(si.getBitmapPath()).length();
}
}
result.put(KEY_DYNAMIC, numDynamic);
result.put(KEY_MANIFEST, numManifest);
result.put(KEY_PINNED, numPinned);
result.put(KEY_BITMAPS, numBitmaps);
result.put(KEY_BITMAP_BYTES, totalBitmapSize);
// TODO Log update frequency too.
return result;
}
@Override
public void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {

View File

@@ -21,6 +21,8 @@ import android.util.Slog;
import com.android.internal.util.Preconditions;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -31,6 +33,7 @@ import java.io.IOException;
*/
abstract class ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
private static final String KEY_NAME = "name";
private final int mPackageUserId;
private final String mPackageName;
@@ -137,6 +140,12 @@ abstract class ShortcutPackageItem {
public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException;
public JSONObject dumpCheckin(boolean clear) throws JSONException {
final JSONObject result = new JSONObject();
result.put(KEY_NAME, mPackageName);
return result;
}
/**
* Verify various internal states.
*/

View File

@@ -95,6 +95,9 @@ import com.android.server.pm.ShortcutUser.PackageWithUser;
import libcore.io.IoUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -184,6 +187,10 @@ public class ShortcutService extends IShortcutService.Stub {
private static final String LAUNCHER_INTENT_CATEGORY = Intent.CATEGORY_LAUNCHER;
private static final String KEY_SHORTCUT = "shortcut";
private static final String KEY_LOW_RAM = "lowRam";
private static final String KEY_ICON_SIZE = "iconSize";
@VisibleForTesting
interface ConfigConstants {
/**
@@ -1352,10 +1359,18 @@ public class ShortcutService extends IShortcutService.Stub {
if (isCallerSystem()) {
return;
}
injectEnforceCallingPermission(
enforceCallingOrSelfPermission(
android.Manifest.permission.RESET_SHORTCUT_MANAGER_THROTTLING, null);
}
private void enforceCallingOrSelfPermission(
@NonNull String permission, @Nullable String message) {
if (isCallerSystem()) {
return;
}
injectEnforceCallingPermission(permission, message);
}
/**
* Somehow overriding ServiceContext.enforceCallingPermission() in the unit tests would confuse
* mockito. So instead we extracted it here and override it in the tests.
@@ -2981,20 +2996,29 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump UserManager from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " without permission "
+ android.Manifest.permission.DUMP);
return;
enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
"can't dump by this caller");
boolean checkin = false;
boolean clear = false;
if (args != null) {
for (String arg : args) {
if ("-c".equals(arg)) {
checkin = true;
} else if ("--checkin".equals(arg)) {
checkin = true;
clear = true;
}
}
}
if (checkin) {
dumpCheckin(pw, clear);
} else {
dumpInner(pw);
}
dumpInner(pw, args);
}
@VisibleForTesting
void dumpInner(PrintWriter pw, String[] args) {
private void dumpInner(PrintWriter pw) {
synchronized (mLock) {
final long now = injectCurrentTimeMillis();
pw.print("Now: [");
@@ -3106,6 +3130,34 @@ public class ShortcutService extends IShortcutService.Stub {
(count == 0 ? 0 : ((double) dur) / count)));
}
/**
* Dumpsys for checkin.
*
* @param clear if true, clear the history information. Some other system services have this
* behavior but shortcut service doesn't for now.
*/
private void dumpCheckin(PrintWriter pw, boolean clear) {
synchronized (mLock) {
try {
final JSONArray users = new JSONArray();
for (int i = 0; i < mUsers.size(); i++) {
users.put(mUsers.valueAt(i).dumpCheckin(clear));
}
final JSONObject result = new JSONObject();
result.put(KEY_SHORTCUT, users);
result.put(KEY_LOW_RAM, injectIsLowRamDevice());
result.put(KEY_ICON_SIZE, mMaxIconDimension);
pw.println(result.toString(1));
} catch (JSONException e) {
Slog.e(TAG, "Unable to write in json", e);
}
}
}
// === Shell support ===
@Override

View File

@@ -32,6 +32,9 @@ import com.android.internal.util.Preconditions;
import libcore.util.Objects;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -55,6 +58,9 @@ class ShortcutUser {
private static final String ATTR_VALUE = "value";
private static final String ATTR_KNOWN_LOCALES = "locales";
private static final String ATTR_LAST_APP_SCAN_TIME = "last-app-scan-time";
private static final String KEY_USER_ID = "userId";
private static final String KEY_LAUNCHERS = "launchers";
private static final String KEY_PACKAGES = "packages";
static final class PackageWithUser {
final int userId;
@@ -503,4 +509,28 @@ class ShortcutUser {
pw.print(Formatter.formatFileSize(mService.mContext, size));
pw.println(")");
}
public JSONObject dumpCheckin(boolean clear) throws JSONException {
final JSONObject result = new JSONObject();
result.put(KEY_USER_ID, mUserId);
{
final JSONArray launchers = new JSONArray();
for (int i = 0; i < mLaunchers.size(); i++) {
launchers.put(mLaunchers.valueAt(i).dumpCheckin(clear));
}
result.put(KEY_LAUNCHERS, launchers);
}
{
final JSONArray packages = new JSONArray();
for (int i = 0; i < mPackages.size(); i++) {
packages.put(mPackages.valueAt(i).dumpCheckin(clear));
}
result.put(KEY_PACKAGES, packages);
}
return result;
}
}

View File

@@ -0,0 +1,105 @@
{
"shortcut": [
{
"userId": 0,
"launchers": [
{
"name": "com.android.launcher.1"
},
{
"name": "com.android.launcher.2"
},
{
"name": "com.android.launcher.3"
},
{
"name": "com.android.launcher.4"
},
{
"name": "com.android.launcher.1"
}
],
"packages": [
{
"name": "com.android.test.1",
"dynamic": 3,
"manifest": 0,
"pinned": 4,
"bitmaps": 0,
"bitmapBytes": 0
},
{
"name": "com.android.test.2",
"dynamic": 4,
"manifest": 0,
"pinned": 5,
"bitmaps": 2,
"bitmapBytes": ***BITMAP_SIZE***
},
{
"name": "com.android.test.3",
"dynamic": 3,
"manifest": 0,
"pinned": 6,
"bitmaps": 0,
"bitmapBytes": 0
},
{
"name": "com.android.test.4",
"dynamic": 0,
"manifest": 0,
"pinned": 0,
"bitmaps": 0,
"bitmapBytes": 0
}
]
},
{
"userId": 10,
"launchers": [
{
"name": "com.android.launcher.1"
}
],
"packages": [
{
"name": "com.android.test.1",
"dynamic": 3,
"manifest": 0,
"pinned": 2,
"bitmaps": 0,
"bitmapBytes": 0
}
]
},
{
"userId": 20,
"launchers": [
{
"name": "com.android.launcher.1"
},
{
"name": "com.android.launcher.2"
},
{
"name": "com.android.launcher.3"
},
{
"name": "com.android.launcher.1"
}
],
"packages": [
{
"name": "com.android.test.1",
"dynamic": 3,
"manifest": 0,
"pinned": 6,
"bitmaps": 0,
"bitmapBytes": 0
}
]
}
],
"lowRam": false,
"iconSize": 128
}

View File

@@ -93,6 +93,8 @@ import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -1106,17 +1108,32 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected void dumpsysOnLogcat(String message, boolean force) {
if (force || !ENABLE_DUMP) return;
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintWriter pw = new PrintWriter(out);
mService.dumpInner(pw, null);
pw.close();
Log.v(TAG, "Dumping ShortcutService: " + message);
for (String line : out.toString().split("\n")) {
for (String line : dumpsys(null).split("\n")) {
Log.v(TAG, line);
}
}
protected String dumpCheckin() {
return dumpsys(new String[]{"--checkin"});
}
private String dumpsys(String[] args) {
final ArrayList<String> origPermissions = new ArrayList<>(mCallerPermissions);
mCallerPermissions.add(android.Manifest.permission.DUMP);
try {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintWriter pw = new PrintWriter(out);
mService.dump(/* fd */ null, pw, args);
pw.close();
return out.toString();
} finally {
mCallerPermissions.clear();
mCallerPermissions.addAll(origPermissions);
}
}
/**
* For debugging, dump arbitrary file on logcat.
*/
@@ -1793,4 +1810,18 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
}
return actualShortcuts;
}
public String readTestAsset(String assetPath) throws IOException {
final StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(
getTestContext().getResources().getAssets().open(assetPath)))) {
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
sb.append(System.lineSeparator());
}
}
return sb.toString();
}
}

View File

@@ -47,6 +47,10 @@ import android.test.suitebuilder.annotation.SmallTest;
import com.android.frameworks.servicestests.R;
import com.android.server.pm.ShortcutService.ConfigConstants;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Locale;
/**
@@ -1852,4 +1856,46 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
ShortcutInfo.lookUpResourceId(res, "drawable/black_16x64", null,
getTestContext().getPackageName()));
}
public void testDumpCheckin() throws IOException {
prepareCrossProfileDataSet();
// prepareCrossProfileDataSet() doesn't set any icons, so do set here.
final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_32x32));
final Icon bmp64x64 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_64x64));
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("res32x32", res32x32),
makeShortcutWithIcon("res64x64", res64x64),
makeShortcutWithIcon("bmp32x32", bmp32x32),
makeShortcutWithIcon("bmp64x64", bmp64x64))));
});
// We can't predict the compressed bitmap sizes, so get the real sizes here.
final long bitmapTotal =
new File(getPackageShortcut(CALLING_PACKAGE_2, "bmp32x32", USER_0)
.getBitmapPath()).length() +
new File(getPackageShortcut(CALLING_PACKAGE_2, "bmp64x64", USER_0)
.getBitmapPath()).length();
// Read the expected output and inject the bitmap size.
final String expected = readTestAsset("shortcut/dumpsys_expected.txt")
.replace("***BITMAP_SIZE***", String.valueOf(bitmapTotal));
assertEquals(expected, dumpCheckin());
}
public void testDumpsysNoPermission() {
assertExpectException(SecurityException.class, "android.permission.DUMP",
() -> mService.dump(null, new PrintWriter(new StringWriter()), null));
// System can call it without the permission.
runWithSystemUid(() -> {
mService.dump(null, new PrintWriter(new StringWriter()), null);
});
}
}