diff options
| -rw-r--r-- | include/utils/ResourceTypes.h | 2 | ||||
| -rw-r--r-- | libs/utils/ResourceTypes.cpp | 87 | ||||
| -rw-r--r-- | services/java/com/android/server/BackupManagerService.java | 49 | ||||
| -rw-r--r-- | services/java/com/android/server/PackageManagerBackupAgent.java | 69 | ||||
| -rw-r--r-- | tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java | 134 | ||||
| -rw-r--r-- | tools/aapt/Bundle.h | 4 | ||||
| -rw-r--r-- | tools/aapt/Command.cpp | 27 | ||||
| -rw-r--r-- | tools/aapt/Main.cpp | 6 |
8 files changed, 326 insertions, 52 deletions
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 9b8c302..68f9233 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1781,7 +1781,7 @@ public: void getLocales(Vector<String8>* locales) const; #ifndef HAVE_ANDROID_OS - void print() const; + void print(bool inclValues) const; #endif private: diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 3d12dca..69d47f0 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -3830,9 +3830,45 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, #define CHAR16_ARRAY_EQ(constant, var, len) \ ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) -void ResTable::print() const +void print_complex(uint32_t complex, bool isFraction) +{ + const float MANTISSA_MULT = + 1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT); + const float RADIX_MULTS[] = { + 1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT, + 1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT + }; + + float value = (complex&(Res_value::COMPLEX_MANTISSA_MASK + <<Res_value::COMPLEX_MANTISSA_SHIFT)) + * RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT) + & Res_value::COMPLEX_RADIX_MASK]; + printf("%f", value); + + if (isFraction) { + switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { + case Res_value::COMPLEX_UNIT_PX: printf("px"); break; + case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break; + case Res_value::COMPLEX_UNIT_SP: printf("sp"); break; + case Res_value::COMPLEX_UNIT_PT: printf("pt"); break; + case Res_value::COMPLEX_UNIT_IN: printf("in"); break; + case Res_value::COMPLEX_UNIT_MM: printf("mm"); break; + default: printf(" (unknown unit)"); break; + } + } else { + switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { + case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break; + case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break; + default: printf(" (unknown unit)"); break; + } + } +} + +void ResTable::print(bool inclValues) const { - printf("mError=0x%x (%s)\n", mError, strerror(mError)); + if (mError != 0) { + printf("mError=0x%x (%s)\n", mError, strerror(mError)); + } #if 0 printf("mParams=%c%c-%c%c,\n", mParams.language[0], mParams.language[1], @@ -3947,6 +3983,8 @@ void ResTable::print() const (void*)(entriesStart + thisOffset)); continue; } + + const Res_value* value = NULL; if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { printf("<bag>"); } else { @@ -3962,7 +4000,7 @@ void ResTable::print() const continue; } - const Res_value* value = (const Res_value*) + value = (const Res_value*) (((const uint8_t*)ent) + esize); printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", (int)value->dataType, (int)dtohl(value->data), @@ -3973,6 +4011,49 @@ void ResTable::print() const printf(" (PUBLIC)"); } printf("\n"); + + if (inclValues) { + if (value != NULL) { + printf(" "); + if (value->dataType == Res_value::TYPE_NULL) { + printf("(null)\n"); + } else if (value->dataType == Res_value::TYPE_REFERENCE) { + printf("(reference) 0x%08x\n", value->data); + } else if (value->dataType == Res_value::TYPE_ATTRIBUTE) { + printf("(attribute) 0x%08x\n", value->data); + } else if (value->dataType == Res_value::TYPE_STRING) { + size_t len; + const char16_t* str = pkg->header->values.stringAt( + value->data, &len); + if (str == NULL) { + printf("(string) null\n"); + } else { + printf("(string) \"%s\"\n", + String8(str, len).string()); + } + } else if (value->dataType == Res_value::TYPE_FLOAT) { + printf("(float) %g\n", *(const float*)&value->data); + } else if (value->dataType == Res_value::TYPE_DIMENSION) { + printf("(dimension) "); + print_complex(value->data, false); + printf("\n"); + } else if (value->dataType == Res_value::TYPE_FRACTION) { + printf("(fraction) "); + print_complex(value->data, true); + printf("\n"); + } else if (value->dataType >= Res_value::TYPE_FIRST_COLOR_INT + || value->dataType <= Res_value::TYPE_LAST_COLOR_INT) { + printf("(color) #%08x\n", value->data); + } else if (value->dataType == Res_value::TYPE_INT_BOOLEAN) { + printf("(boolean) %s\n", value->data ? "true" : "false"); + } else if (value->dataType >= Res_value::TYPE_FIRST_INT + || value->dataType <= Res_value::TYPE_LAST_INT) { + printf("(int) 0x%08x or %d\n", value->data, value->data); + } else { + printf("(unknown type)\n"); + } + } + } } } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index fa88111..75e0e64 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -54,6 +54,7 @@ import com.android.internal.backup.LocalTransport; import com.android.internal.backup.IBackupTransport; import com.android.server.PackageManagerBackupAgent; +import com.android.server.PackageManagerBackupAgent.Metadata; import java.io.EOFException; import java.io.File; @@ -111,9 +112,8 @@ class BackupManagerService extends IBackupManager.Stub { // Do we need to back up the package manager metadata on the next pass? private boolean mDoPackageManager; private static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; - // Backups that we have started. These are separate to prevent starvation - // if an app keeps re-enqueuing itself. - private ArrayList<BackupRequest> mBackupQueue; + + // locking around the pending-backup management private final Object mQueueLock = new Object(); // The thread performing the sequence of queued backups binds to each app's agent @@ -296,6 +296,7 @@ class BackupManagerService extends IBackupManager.Stub { } // snapshot the pending-backup set and work on that + ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>(); File oldJournal = mJournal; synchronized (mQueueLock) { if (mPendingBackups.size() == 0) { @@ -303,13 +304,11 @@ class BackupManagerService extends IBackupManager.Stub { break; } - if (mBackupQueue == null) { - mBackupQueue = new ArrayList<BackupRequest>(); - for (BackupRequest b: mPendingBackups.values()) { - mBackupQueue.add(b); - } - mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>(); + for (BackupRequest b: mPendingBackups.values()) { + queue.add(b); } + Log.v(TAG, "clearing pending backups"); + mPendingBackups.clear(); // Start a new backup-queue journal file too if (mJournalStream != null) { @@ -328,7 +327,7 @@ class BackupManagerService extends IBackupManager.Stub { // at next boot and the journaled requests fulfilled. } - (new PerformBackupThread(transport, mBackupQueue, oldJournal)).start(); + (new PerformBackupThread(transport, queue, oldJournal)).start(); break; } @@ -759,6 +758,8 @@ class BackupManagerService extends IBackupManager.Stub { private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) { // Allow unsigned apps, but not signed on one device and unsigned on the other // !!! TODO: is this the right policy? + if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs + + " device=" + deviceSigs); if ((storedSigs == null || storedSigs.length == 0) && (deviceSigs == null || deviceSigs.length == 0)) { return true; @@ -800,6 +801,7 @@ class BackupManagerService extends IBackupManager.Stub { @Override public void run() { + if (DEBUG) Log.v(TAG, "Beginning restore process"); /** * Restore sequence: * @@ -847,13 +849,19 @@ class BackupManagerService extends IBackupManager.Stub { PackageInfo app = isRestorable(pkg); if (app != null) { // Validate against the backed-up signature block, too - Signature[] storedSigs - = pmAgent.getRestoredSignatures(app.packageName); - // !!! TODO: check app version here as well - if (signaturesMatch(storedSigs, app.signatures)) { - appsToRestore.add(app); + Metadata info = pmAgent.getRestoredMetadata(app.packageName); + if (app.versionCode >= info.versionCode) { + if (DEBUG) Log.v(TAG, "Restore version " + info.versionCode + + " compatible with app version " + app.versionCode); + if (signaturesMatch(info.signatures, app.signatures)) { + appsToRestore.add(app); + } else { + Log.w(TAG, "Sig mismatch restoring " + app.packageName); + } } else { - Log.w(TAG, "Sig mismatch on restore of " + app.packageName); + Log.i(TAG, "Restore set for " + app.packageName + + " is too new [" + info.versionCode + + "] for installed app version " + app.versionCode); } } } @@ -1202,11 +1210,10 @@ class BackupManagerService extends IBackupManager.Stub { pw.println(app.toString()); } } - pw.println("Pending:"); - Iterator<BackupRequest> br = mPendingBackups.values().iterator(); - while (br.hasNext()) { - pw.print(" "); - pw.println(br); + pw.println("Pending: " + mPendingBackups.size()); + for (BackupRequest req : mPendingBackups.values()) { + pw.print(" "); + pw.println(req); } } } diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java index 6bd32a0..cc84430 100644 --- a/services/java/com/android/server/PackageManagerBackupAgent.java +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -40,9 +40,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -// !!!TODO: take this out -import java.util.zip.CRC32; - /** * We back up the signatures of each package so that during a system restore, * we can verify that the app whose data we think we have matches the app @@ -57,7 +54,17 @@ public class PackageManagerBackupAgent extends BackupAgent { private List<ApplicationInfo> mAllApps; private PackageManager mPackageManager; - private HashMap<String, Signature[]> mRestoredSignatures; + private HashMap<String, Metadata> mRestoredSignatures; + + public class Metadata { + public int versionCode; + public Signature[] signatures; + + Metadata(int version, Signature[] sigs) { + versionCode = version; + signatures = sigs; + } + } // We're constructed with the set of applications that are participating // in backup. This set changes as apps are installed & removed. @@ -67,7 +74,7 @@ public class PackageManagerBackupAgent extends BackupAgent { mRestoredSignatures = null; } - public Signature[] getRestoredSignatures(String packageName) { + public Metadata getRestoredMetadata(String packageName) { if (mRestoredSignatures == null) { return null; } @@ -83,6 +90,9 @@ public class PackageManagerBackupAgent extends BackupAgent { // For each app we have on device, see if we've backed it up yet. If not, // write its signature block to the output, keyed on the package name. + if (DEBUG) Log.v(TAG, "onBackup()"); + ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these + DataOutputStream outWriter = new DataOutputStream(bufStream); for (ApplicationInfo app : mAllApps) { String packName = app.packageName; if (!existing.contains(packName)) { @@ -90,16 +100,27 @@ public class PackageManagerBackupAgent extends BackupAgent { try { PackageInfo info = mPackageManager.getPackageInfo(packName, PackageManager.GET_SIGNATURES); - // build a byte array out of the signature list + /* + * Metadata for each package: + * + * int version -- [4] the package's versionCode + * byte[] signatures -- [len] flattened Signature[] of the package + */ + + // marshall the version code in a canonical form + bufStream.reset(); + outWriter.writeInt(info.versionCode); + byte[] versionBuf = bufStream.toByteArray(); + byte[] sigs = flattenSignatureArray(info.signatures); -// !!! TODO: take out this debugging + + // !!! TODO: take out this debugging if (DEBUG) { - CRC32 crc = new CRC32(); - crc.update(sigs); - Log.i(TAG, "+ flat sig array for " + packName + " : " - + crc.getValue()); + Log.v(TAG, "+ metadata for " + packName + " version=" + info.versionCode); } - data.writeEntityHeader(packName, sigs.length); + // Now we can write the backup entity for this package + data.writeEntityHeader(packName, versionBuf.length + sigs.length); + data.writeEntityData(versionBuf, versionBuf.length); data.writeEntityData(sigs, sigs.length); } catch (NameNotFoundException e) { // Weird; we just found it, and now are told it doesn't exist. @@ -113,6 +134,8 @@ public class PackageManagerBackupAgent extends BackupAgent { } else { // We've already backed up this app. Remove it from the set so // we can tell at the end what has disappeared from the device. + // !!! TODO: take out the debugging message + if (DEBUG) Log.v(TAG, "= already backed up metadata for " + packName); if (!existing.remove(packName)) { Log.d(TAG, "*** failed to remove " + packName + " from package set!"); } @@ -123,6 +146,8 @@ public class PackageManagerBackupAgent extends BackupAgent { // mentioned in the saved state file, but appear to no longer be present // on the device. Write a deletion entity for them. for (String app : existing) { + // !!! TODO: take out this msg + if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app); try { data.writeEntityHeader(app, -1); } catch (IOException e) { @@ -141,27 +166,29 @@ public class PackageManagerBackupAgent extends BackupAgent { public void onRestore(BackupDataInput data, ParcelFileDescriptor newState) throws IOException { List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); - HashMap<String, Signature[]> sigMap = new HashMap<String, Signature[]>(); + HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); while (data.readNextHeader()) { int dataSize = data.getDataSize(); byte[] buf = new byte[dataSize]; data.readEntityData(buf, 0, dataSize); - Signature[] sigs = unflattenSignatureArray(buf); + ByteArrayInputStream bufStream = new ByteArrayInputStream(buf); + DataInputStream in = new DataInputStream(bufStream); + int versionCode = in.readInt(); + + Signature[] sigs = unflattenSignatureArray(in); String pkg = data.getKey(); // !!! TODO: take out this debugging if (DEBUG) { - CRC32 crc = new CRC32(); - crc.update(buf); - Log.i(TAG, "- unflat sig array for " + pkg + " : " - + crc.getValue()); + Log.i(TAG, "+ restored metadata for " + pkg + + " versionCode=" + versionCode + " sigs=" + sigs); } ApplicationInfo app = new ApplicationInfo(); app.packageName = pkg; restoredApps.add(app); - sigMap.put(pkg, sigs); + sigMap.put(pkg, new Metadata(versionCode, sigs)); } mRestoredSignatures = sigMap; @@ -193,9 +220,7 @@ public class PackageManagerBackupAgent extends BackupAgent { return outBuf.toByteArray(); } - private Signature[] unflattenSignatureArray(byte[] buffer) { - ByteArrayInputStream inBufStream = new ByteArrayInputStream(buffer); - DataInputStream in = new DataInputStream(inBufStream); + private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) { Signature[] sigs = null; try { diff --git a/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java new file mode 100644 index 0000000..dc6860a --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2009 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.framework.permission.tests; + +import junit.framework.TestCase; + +import android.os.Binder; +import android.os.IHardwareService; +import android.os.RemoteException; +import android.os.ServiceManager; + +/** + * Verify that Hardware apis cannot be called without required permissions. + */ +public class HardwareServicePermissionTest extends TestCase { + + private IHardwareService mHardwareService; + + @Override + protected void setUp() throws Exception { + mHardwareService = IHardwareService.Stub.asInterface( + ServiceManager.getService("hardware")); + } + + /** + * Test that calling {@link android.os.IHardwareService#vibrate(long)} requires permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#VIBRATE} + * @throws RemoteException + */ + public void testVibrate() throws RemoteException { + try { + mHardwareService.vibrate(2000); + fail("vibrate did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#vibratePattern(long[], + * int, android.os.IBinder)} requires permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#VIBRATE} + * @throws RemoteException + */ + public void testVibratePattern() throws RemoteException { + try { + mHardwareService.vibratePattern(new long[] {0}, 0, new Binder()); + fail("vibratePattern did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#cancelVibrate()} requires permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#VIBRATE} + * @throws RemoteException + */ + public void testCancelVibrate() throws RemoteException { + try { + mHardwareService.cancelVibrate(); + fail("cancelVibrate did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#setFlashlightEnabled(boolean)} + * requires permissions. + * <p>Tests permissions: + * {@link android.Manifest.permission#HARDWARE_TEST} + * {@link android.Manifest.permission#FLASHLIGHT} + * @throws RemoteException + */ + public void testSetFlashlightEnabled() throws RemoteException { + try { + mHardwareService.setFlashlightEnabled(true); + fail("setFlashlightEnabled did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#enableCameraFlash(int)} requires + * permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#HARDWARE_TEST} + * {@link android.Manifest.permission#CAMERA} + * @throws RemoteException + */ + public void testEnableCameraFlash() throws RemoteException { + try { + mHardwareService.enableCameraFlash(100); + fail("enableCameraFlash did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#setBacklights(int)} requires + * permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#HARDWARE_TEST} + * @throws RemoteException + */ + public void testSetBacklights() throws RemoteException { + try { + mHardwareService.setBacklights(0); + fail("setBacklights did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } +} diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index 2eb7a1d..a6fedf3 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -34,6 +34,7 @@ public: mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false), mUpdate(false), mExtending(false), mRequireLocalization(false), mPseudolocalize(false), + mValues(false), mCompressionMethod(0), mOutputAPKFile(NULL), mAssetSourceDir(NULL), mAndroidManifestFile(NULL), mPublicOutputFile(NULL), @@ -72,6 +73,8 @@ public: void setRequireLocalization(bool val) { mRequireLocalization = val; } bool getPseudolocalize(void) const { return mPseudolocalize; } void setPseudolocalize(bool val) { mPseudolocalize = val; } + bool getValues(void) const { return mValues; } + void setValues(bool val) { mValues = val; } int getCompressionMethod(void) const { return mCompressionMethod; } void setCompressionMethod(int val) { mCompressionMethod = val; } const char* getOutputAPKFile() const { return mOutputAPKFile; } @@ -151,6 +154,7 @@ private: bool mExtending; bool mRequireLocalization; bool mPseudolocalize; + bool mValues; int mCompressionMethod; const char* mOutputAPKFile; const char* mAssetSourceDir; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index dc91a48..503f661 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -196,7 +196,7 @@ int doList(Bundle* bundle) printf("\nNo resource table found.\n"); } else { printf("\nResource table:\n"); - res.print(); + res.print(false); } Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", @@ -380,7 +380,7 @@ int doDump(Bundle* bundle) } if (strcmp("resources", option) == 0) { - res.print(); + res.print(bundle->getValues()); } else if (strcmp("xmltree", option) == 0) { if (bundle->getFileSpecCount() < 3) { @@ -732,11 +732,12 @@ int doDump(Bundle* bundle) activityIcon.string()); } } + printf("locales:"); Vector<String8> locales; res.getLocales(&locales); - const size_t N = locales.size(); - for (size_t i=0; i<N; i++) { + const size_t NL = locales.size(); + for (size_t i=0; i<NL; i++) { const char* localeStr = locales[i].string(); if (localeStr == NULL || strlen(localeStr) == 0) { localeStr = "--_--"; @@ -744,6 +745,24 @@ int doDump(Bundle* bundle) printf(" '%s'", localeStr); } printf("\n"); + + Vector<ResTable_config> configs; + res.getConfigurations(&configs); + SortedVector<int> densities; + const size_t NC = configs.size(); + for (size_t i=0; i<NC; i++) { + int dens = configs[i].density; + if (dens == 0) dens = 160; + densities.add(dens); + } + + printf("densities:"); + const size_t ND = densities.size(); + for (size_t i=0; i<ND; i++) { + printf(" '%d'", densities[i]); + } + printf("\n"); + AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); if (dir != NULL) { if (dir->getFileCount() > 0) { diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 8bf2b07..12a0445 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -45,7 +45,7 @@ void usage(void) " %s l[ist] [-v] [-a] file.{zip,jar,apk}\n" " List contents of Zip-compatible archive.\n\n", gProgName); fprintf(stderr, - " %s d[ump] WHAT file.{apk} [asset [asset ...]]\n" + " %s d[ump] [--values] WHAT file.{apk} [asset [asset ...]]\n" " badging Print the label and icon for the app declared in APK.\n" " permissions Print the permissions from the APK.\n" " resources Print the resource table from the APK.\n" @@ -123,6 +123,8 @@ void usage(void) " inserts android:targetSdkVersion in to manifest.\n" " --max-sdk-version\n" " inserts android:maxSdkVersion in to manifest.\n" + " --values\n" + " when used with \"dump resources\" also includes resource values.\n" " --version-code\n" " inserts android:versionCode in to manifest.\n" " --version-name\n" @@ -396,6 +398,8 @@ int main(int argc, char* const argv[]) goto bail; } bundle.setVersionName(argv[0]); + } else if (strcmp(cp, "-values") == 0) { + bundle.setValues(true); } else { fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp); wantUsage = true; |
