PackageParser.java revision 45515659438964ec47f4feac247f0e9dce587c86
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.content.pm; 18 19import org.xmlpull.v1.XmlPullParser; 20import org.xmlpull.v1.XmlPullParserException; 21 22import android.content.ComponentName; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.content.res.AssetManager; 26import android.content.res.Configuration; 27import android.content.res.Resources; 28import android.content.res.TypedArray; 29import android.content.res.XmlResourceParser; 30import android.os.Bundle; 31import android.os.PatternMatcher; 32import android.util.AttributeSet; 33import android.util.Config; 34import android.util.DisplayMetrics; 35import android.util.Log; 36import android.util.TypedValue; 37import com.android.internal.util.XmlUtils; 38 39import java.io.File; 40import java.io.IOException; 41import java.io.InputStream; 42import java.lang.ref.WeakReference; 43import java.security.cert.Certificate; 44import java.security.cert.CertificateEncodingException; 45import java.util.ArrayList; 46import java.util.Enumeration; 47import java.util.Iterator; 48import java.util.List; 49import java.util.jar.JarEntry; 50import java.util.jar.JarFile; 51 52/** 53 * Package archive parsing 54 * 55 * {@hide} 56 */ 57public class PackageParser { 58 /** @hide */ 59 public static class NewPermissionInfo { 60 public final String name; 61 public final int sdkVersion; 62 public final int fileVersion; 63 64 public NewPermissionInfo(String name, int sdkVersion, int fileVersion) { 65 this.name = name; 66 this.sdkVersion = sdkVersion; 67 this.fileVersion = fileVersion; 68 } 69 } 70 71 /** 72 * List of new permissions that have been added since 1.0. 73 * NOTE: These must be declared in SDK version order, with permissions 74 * added to older SDKs appearing before those added to newer SDKs. 75 * @hide 76 */ 77 public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] = 78 new PackageParser.NewPermissionInfo[] { 79 new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_SDCARD, 80 android.os.Build.VERSION_CODES.DONUT, 0), 81 new PackageParser.NewPermissionInfo(android.Manifest.permission.READ_PHONE_STATE, 82 android.os.Build.VERSION_CODES.DONUT, 0) 83 }; 84 85 private String mArchiveSourcePath; 86 private String[] mSeparateProcesses; 87 private int mSdkVersion; 88 private String mSdkCodename; 89 90 private int mParseError = PackageManager.INSTALL_SUCCEEDED; 91 92 private static final Object mSync = new Object(); 93 private static WeakReference<byte[]> mReadBuffer; 94 95 static class ParsePackageItemArgs { 96 final Package owner; 97 final String[] outError; 98 final int nameRes; 99 final int labelRes; 100 final int iconRes; 101 102 String tag; 103 TypedArray sa; 104 105 ParsePackageItemArgs(Package _owner, String[] _outError, 106 int _nameRes, int _labelRes, int _iconRes) { 107 owner = _owner; 108 outError = _outError; 109 nameRes = _nameRes; 110 labelRes = _labelRes; 111 iconRes = _iconRes; 112 } 113 } 114 115 static class ParseComponentArgs extends ParsePackageItemArgs { 116 final String[] sepProcesses; 117 final int processRes; 118 final int enabledRes; 119 int flags; 120 121 ParseComponentArgs(Package _owner, String[] _outError, 122 int _nameRes, int _labelRes, int _iconRes, 123 String[] _sepProcesses, int _processRes,int _enabledRes) { 124 super(_owner, _outError, _nameRes, _labelRes, _iconRes); 125 sepProcesses = _sepProcesses; 126 processRes = _processRes; 127 enabledRes = _enabledRes; 128 } 129 } 130 131 private ParsePackageItemArgs mParseInstrumentationArgs; 132 private ParseComponentArgs mParseActivityArgs; 133 private ParseComponentArgs mParseActivityAliasArgs; 134 private ParseComponentArgs mParseServiceArgs; 135 private ParseComponentArgs mParseProviderArgs; 136 137 /** If set to true, we will only allow package files that exactly match 138 * the DTD. Otherwise, we try to get as much from the package as we 139 * can without failing. This should normally be set to false, to 140 * support extensions to the DTD in future versions. */ 141 private static final boolean RIGID_PARSER = false; 142 143 private static final String TAG = "PackageParser"; 144 145 public PackageParser(String archiveSourcePath) { 146 mArchiveSourcePath = archiveSourcePath; 147 } 148 149 public void setSeparateProcesses(String[] procs) { 150 mSeparateProcesses = procs; 151 } 152 153 public void setSdkVersion(int sdkVersion, String codename) { 154 mSdkVersion = sdkVersion; 155 mSdkCodename = codename; 156 } 157 158 private static final boolean isPackageFilename(String name) { 159 return name.endsWith(".apk"); 160 } 161 162 /** 163 * Generate and return the {@link PackageInfo} for a parsed package. 164 * 165 * @param p the parsed package. 166 * @param flags indicating which optional information is included. 167 */ 168 public static PackageInfo generatePackageInfo(PackageParser.Package p, 169 int gids[], int flags) { 170 171 PackageInfo pi = new PackageInfo(); 172 pi.packageName = p.packageName; 173 pi.versionCode = p.mVersionCode; 174 pi.versionName = p.mVersionName; 175 pi.sharedUserId = p.mSharedUserId; 176 pi.sharedUserLabel = p.mSharedUserLabel; 177 pi.applicationInfo = p.applicationInfo; 178 if ((flags&PackageManager.GET_GIDS) != 0) { 179 pi.gids = gids; 180 } 181 if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) { 182 int N = p.configPreferences.size(); 183 if (N > 0) { 184 pi.configPreferences = new ConfigurationInfo[N]; 185 for (int i=0; i<N; i++) { 186 pi.configPreferences[i] = p.configPreferences.get(i); 187 } 188 } 189 } 190 if ((flags&PackageManager.GET_ACTIVITIES) != 0) { 191 int N = p.activities.size(); 192 if (N > 0) { 193 pi.activities = new ActivityInfo[N]; 194 for (int i=0; i<N; i++) { 195 final Activity activity = p.activities.get(i); 196 if (activity.info.enabled 197 || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { 198 pi.activities[i] = generateActivityInfo(p.activities.get(i), flags); 199 } 200 } 201 } 202 } 203 if ((flags&PackageManager.GET_RECEIVERS) != 0) { 204 int N = p.receivers.size(); 205 if (N > 0) { 206 pi.receivers = new ActivityInfo[N]; 207 for (int i=0; i<N; i++) { 208 final Activity activity = p.receivers.get(i); 209 if (activity.info.enabled 210 || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { 211 pi.receivers[i] = generateActivityInfo(p.receivers.get(i), flags); 212 } 213 } 214 } 215 } 216 if ((flags&PackageManager.GET_SERVICES) != 0) { 217 int N = p.services.size(); 218 if (N > 0) { 219 pi.services = new ServiceInfo[N]; 220 for (int i=0; i<N; i++) { 221 final Service service = p.services.get(i); 222 if (service.info.enabled 223 || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { 224 pi.services[i] = generateServiceInfo(p.services.get(i), flags); 225 } 226 } 227 } 228 } 229 if ((flags&PackageManager.GET_PROVIDERS) != 0) { 230 int N = p.providers.size(); 231 if (N > 0) { 232 pi.providers = new ProviderInfo[N]; 233 for (int i=0; i<N; i++) { 234 final Provider provider = p.providers.get(i); 235 if (provider.info.enabled 236 || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { 237 pi.providers[i] = generateProviderInfo(p.providers.get(i), flags); 238 } 239 } 240 } 241 } 242 if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) { 243 int N = p.instrumentation.size(); 244 if (N > 0) { 245 pi.instrumentation = new InstrumentationInfo[N]; 246 for (int i=0; i<N; i++) { 247 pi.instrumentation[i] = generateInstrumentationInfo( 248 p.instrumentation.get(i), flags); 249 } 250 } 251 } 252 if ((flags&PackageManager.GET_PERMISSIONS) != 0) { 253 int N = p.permissions.size(); 254 if (N > 0) { 255 pi.permissions = new PermissionInfo[N]; 256 for (int i=0; i<N; i++) { 257 pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags); 258 } 259 } 260 N = p.requestedPermissions.size(); 261 if (N > 0) { 262 pi.requestedPermissions = new String[N]; 263 for (int i=0; i<N; i++) { 264 pi.requestedPermissions[i] = p.requestedPermissions.get(i); 265 } 266 } 267 } 268 if ((flags&PackageManager.GET_SIGNATURES) != 0) { 269 int N = p.mSignatures.length; 270 if (N > 0) { 271 pi.signatures = new Signature[N]; 272 System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N); 273 } 274 } 275 return pi; 276 } 277 278 private Certificate[] loadCertificates(JarFile jarFile, JarEntry je, 279 byte[] readBuffer) { 280 try { 281 // We must read the stream for the JarEntry to retrieve 282 // its certificates. 283 InputStream is = jarFile.getInputStream(je); 284 while (is.read(readBuffer, 0, readBuffer.length) != -1) { 285 // not using 286 } 287 is.close(); 288 return je != null ? je.getCertificates() : null; 289 } catch (IOException e) { 290 Log.w(TAG, "Exception reading " + je.getName() + " in " 291 + jarFile.getName(), e); 292 } 293 return null; 294 } 295 296 public final static int PARSE_IS_SYSTEM = 0x0001; 297 public final static int PARSE_CHATTY = 0x0002; 298 public final static int PARSE_MUST_BE_APK = 0x0004; 299 public final static int PARSE_IGNORE_PROCESSES = 0x0008; 300 301 public int getParseError() { 302 return mParseError; 303 } 304 305 public Package parsePackage(File sourceFile, String destFileName, 306 DisplayMetrics metrics, int flags) { 307 mParseError = PackageManager.INSTALL_SUCCEEDED; 308 309 mArchiveSourcePath = sourceFile.getPath(); 310 if (!sourceFile.isFile()) { 311 Log.w(TAG, "Skipping dir: " + mArchiveSourcePath); 312 mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; 313 return null; 314 } 315 if (!isPackageFilename(sourceFile.getName()) 316 && (flags&PARSE_MUST_BE_APK) != 0) { 317 if ((flags&PARSE_IS_SYSTEM) == 0) { 318 // We expect to have non-.apk files in the system dir, 319 // so don't warn about them. 320 Log.w(TAG, "Skipping non-package file: " + mArchiveSourcePath); 321 } 322 mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; 323 return null; 324 } 325 326 if ((flags&PARSE_CHATTY) != 0 && Config.LOGD) Log.d( 327 TAG, "Scanning package: " + mArchiveSourcePath); 328 329 XmlResourceParser parser = null; 330 AssetManager assmgr = null; 331 boolean assetError = true; 332 try { 333 assmgr = new AssetManager(); 334 int cookie = assmgr.addAssetPath(mArchiveSourcePath); 335 if(cookie != 0) { 336 parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml"); 337 assetError = false; 338 } else { 339 Log.w(TAG, "Failed adding asset path:"+mArchiveSourcePath); 340 } 341 } catch (Exception e) { 342 Log.w(TAG, "Unable to read AndroidManifest.xml of " 343 + mArchiveSourcePath, e); 344 } 345 if(assetError) { 346 if (assmgr != null) assmgr.close(); 347 mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; 348 return null; 349 } 350 String[] errorText = new String[1]; 351 Package pkg = null; 352 Exception errorException = null; 353 try { 354 // XXXX todo: need to figure out correct configuration. 355 Resources res = new Resources(assmgr, metrics, null); 356 pkg = parsePackage(res, parser, flags, errorText); 357 } catch (Exception e) { 358 errorException = e; 359 mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; 360 } 361 362 363 if (pkg == null) { 364 if (errorException != null) { 365 Log.w(TAG, mArchiveSourcePath, errorException); 366 } else { 367 Log.w(TAG, mArchiveSourcePath + " (at " 368 + parser.getPositionDescription() 369 + "): " + errorText[0]); 370 } 371 parser.close(); 372 assmgr.close(); 373 if (mParseError == PackageManager.INSTALL_SUCCEEDED) { 374 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 375 } 376 return null; 377 } 378 379 parser.close(); 380 assmgr.close(); 381 382 pkg.applicationInfo.sourceDir = destFileName; 383 pkg.applicationInfo.publicSourceDir = destFileName; 384 pkg.mSignatures = null; 385 386 return pkg; 387 } 388 389 public boolean collectCertificates(Package pkg, int flags) { 390 pkg.mSignatures = null; 391 392 WeakReference<byte[]> readBufferRef; 393 byte[] readBuffer = null; 394 synchronized (mSync) { 395 readBufferRef = mReadBuffer; 396 if (readBufferRef != null) { 397 mReadBuffer = null; 398 readBuffer = readBufferRef.get(); 399 } 400 if (readBuffer == null) { 401 readBuffer = new byte[8192]; 402 readBufferRef = new WeakReference<byte[]>(readBuffer); 403 } 404 } 405 406 try { 407 JarFile jarFile = new JarFile(mArchiveSourcePath); 408 409 Certificate[] certs = null; 410 411 if ((flags&PARSE_IS_SYSTEM) != 0) { 412 // If this package comes from the system image, then we 413 // can trust it... we'll just use the AndroidManifest.xml 414 // to retrieve its signatures, not validating all of the 415 // files. 416 JarEntry jarEntry = jarFile.getJarEntry("AndroidManifest.xml"); 417 certs = loadCertificates(jarFile, jarEntry, readBuffer); 418 if (certs == null) { 419 Log.e(TAG, "Package " + pkg.packageName 420 + " has no certificates at entry " 421 + jarEntry.getName() + "; ignoring!"); 422 jarFile.close(); 423 mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; 424 return false; 425 } 426 if (false) { 427 Log.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry 428 + " certs=" + (certs != null ? certs.length : 0)); 429 if (certs != null) { 430 final int N = certs.length; 431 for (int i=0; i<N; i++) { 432 Log.i(TAG, " Public key: " 433 + certs[i].getPublicKey().getEncoded() 434 + " " + certs[i].getPublicKey()); 435 } 436 } 437 } 438 439 } else { 440 Enumeration entries = jarFile.entries(); 441 while (entries.hasMoreElements()) { 442 JarEntry je = (JarEntry)entries.nextElement(); 443 if (je.isDirectory()) continue; 444 if (je.getName().startsWith("META-INF/")) continue; 445 Certificate[] localCerts = loadCertificates(jarFile, je, 446 readBuffer); 447 if (false) { 448 Log.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName() 449 + ": certs=" + certs + " (" 450 + (certs != null ? certs.length : 0) + ")"); 451 } 452 if (localCerts == null) { 453 Log.e(TAG, "Package " + pkg.packageName 454 + " has no certificates at entry " 455 + je.getName() + "; ignoring!"); 456 jarFile.close(); 457 mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; 458 return false; 459 } else if (certs == null) { 460 certs = localCerts; 461 } else { 462 // Ensure all certificates match. 463 for (int i=0; i<certs.length; i++) { 464 boolean found = false; 465 for (int j=0; j<localCerts.length; j++) { 466 if (certs[i] != null && 467 certs[i].equals(localCerts[j])) { 468 found = true; 469 break; 470 } 471 } 472 if (!found || certs.length != localCerts.length) { 473 Log.e(TAG, "Package " + pkg.packageName 474 + " has mismatched certificates at entry " 475 + je.getName() + "; ignoring!"); 476 jarFile.close(); 477 mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; 478 return false; 479 } 480 } 481 } 482 } 483 } 484 jarFile.close(); 485 486 synchronized (mSync) { 487 mReadBuffer = readBufferRef; 488 } 489 490 if (certs != null && certs.length > 0) { 491 final int N = certs.length; 492 pkg.mSignatures = new Signature[certs.length]; 493 for (int i=0; i<N; i++) { 494 pkg.mSignatures[i] = new Signature( 495 certs[i].getEncoded()); 496 } 497 } else { 498 Log.e(TAG, "Package " + pkg.packageName 499 + " has no certificates; ignoring!"); 500 mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; 501 return false; 502 } 503 } catch (CertificateEncodingException e) { 504 Log.w(TAG, "Exception reading " + mArchiveSourcePath, e); 505 mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; 506 return false; 507 } catch (IOException e) { 508 Log.w(TAG, "Exception reading " + mArchiveSourcePath, e); 509 mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; 510 return false; 511 } catch (RuntimeException e) { 512 Log.w(TAG, "Exception reading " + mArchiveSourcePath, e); 513 mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; 514 return false; 515 } 516 517 return true; 518 } 519 520 public static String parsePackageName(String packageFilePath, int flags) { 521 XmlResourceParser parser = null; 522 AssetManager assmgr = null; 523 try { 524 assmgr = new AssetManager(); 525 int cookie = assmgr.addAssetPath(packageFilePath); 526 parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml"); 527 } catch (Exception e) { 528 if (assmgr != null) assmgr.close(); 529 Log.w(TAG, "Unable to read AndroidManifest.xml of " 530 + packageFilePath, e); 531 return null; 532 } 533 AttributeSet attrs = parser; 534 String errors[] = new String[1]; 535 String packageName = null; 536 try { 537 packageName = parsePackageName(parser, attrs, flags, errors); 538 } catch (IOException e) { 539 Log.w(TAG, packageFilePath, e); 540 } catch (XmlPullParserException e) { 541 Log.w(TAG, packageFilePath, e); 542 } finally { 543 if (parser != null) parser.close(); 544 if (assmgr != null) assmgr.close(); 545 } 546 if (packageName == null) { 547 Log.e(TAG, "parsePackageName error: " + errors[0]); 548 return null; 549 } 550 return packageName; 551 } 552 553 private static String validateName(String name, boolean requiresSeparator) { 554 final int N = name.length(); 555 boolean hasSep = false; 556 boolean front = true; 557 for (int i=0; i<N; i++) { 558 final char c = name.charAt(i); 559 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { 560 front = false; 561 continue; 562 } 563 if (!front) { 564 if ((c >= '0' && c <= '9') || c == '_') { 565 continue; 566 } 567 } 568 if (c == '.') { 569 hasSep = true; 570 front = true; 571 continue; 572 } 573 return "bad character '" + c + "'"; 574 } 575 return hasSep || !requiresSeparator 576 ? null : "must have at least one '.' separator"; 577 } 578 579 private static String parsePackageName(XmlPullParser parser, 580 AttributeSet attrs, int flags, String[] outError) 581 throws IOException, XmlPullParserException { 582 583 int type; 584 while ((type=parser.next()) != parser.START_TAG 585 && type != parser.END_DOCUMENT) { 586 ; 587 } 588 589 if (type != parser.START_TAG) { 590 outError[0] = "No start tag found"; 591 return null; 592 } 593 if ((flags&PARSE_CHATTY) != 0 && Config.LOGV) Log.v( 594 TAG, "Root element name: '" + parser.getName() + "'"); 595 if (!parser.getName().equals("manifest")) { 596 outError[0] = "No <manifest> tag"; 597 return null; 598 } 599 String pkgName = attrs.getAttributeValue(null, "package"); 600 if (pkgName == null || pkgName.length() == 0) { 601 outError[0] = "<manifest> does not specify package"; 602 return null; 603 } 604 String nameError = validateName(pkgName, true); 605 if (nameError != null && !"android".equals(pkgName)) { 606 outError[0] = "<manifest> specifies bad package name \"" 607 + pkgName + "\": " + nameError; 608 return null; 609 } 610 611 return pkgName.intern(); 612 } 613 614 /** 615 * Temporary. 616 */ 617 static public Signature stringToSignature(String str) { 618 final int N = str.length(); 619 byte[] sig = new byte[N]; 620 for (int i=0; i<N; i++) { 621 sig[i] = (byte)str.charAt(i); 622 } 623 return new Signature(sig); 624 } 625 626 private Package parsePackage( 627 Resources res, XmlResourceParser parser, int flags, String[] outError) 628 throws XmlPullParserException, IOException { 629 AttributeSet attrs = parser; 630 631 mParseInstrumentationArgs = null; 632 mParseActivityArgs = null; 633 mParseServiceArgs = null; 634 mParseProviderArgs = null; 635 636 String pkgName = parsePackageName(parser, attrs, flags, outError); 637 if (pkgName == null) { 638 mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; 639 return null; 640 } 641 int type; 642 643 final Package pkg = new Package(pkgName); 644 boolean foundApp = false; 645 646 TypedArray sa = res.obtainAttributes(attrs, 647 com.android.internal.R.styleable.AndroidManifest); 648 pkg.mVersionCode = sa.getInteger( 649 com.android.internal.R.styleable.AndroidManifest_versionCode, 0); 650 pkg.mVersionName = sa.getNonResourceString( 651 com.android.internal.R.styleable.AndroidManifest_versionName); 652 if (pkg.mVersionName != null) { 653 pkg.mVersionName = pkg.mVersionName.intern(); 654 } 655 String str = sa.getNonResourceString( 656 com.android.internal.R.styleable.AndroidManifest_sharedUserId); 657 if (str != null) { 658 String nameError = validateName(str, true); 659 if (nameError != null && !"android".equals(pkgName)) { 660 outError[0] = "<manifest> specifies bad sharedUserId name \"" 661 + str + "\": " + nameError; 662 mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID; 663 return null; 664 } 665 pkg.mSharedUserId = str.intern(); 666 pkg.mSharedUserLabel = sa.getResourceId( 667 com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0); 668 } 669 sa.recycle(); 670 671 int outerDepth = parser.getDepth(); 672 while ((type=parser.next()) != parser.END_DOCUMENT 673 && (type != parser.END_TAG || parser.getDepth() > outerDepth)) { 674 if (type == parser.END_TAG || type == parser.TEXT) { 675 continue; 676 } 677 678 String tagName = parser.getName(); 679 if (tagName.equals("application")) { 680 if (foundApp) { 681 if (RIGID_PARSER) { 682 outError[0] = "<manifest> has more than one <application>"; 683 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 684 return null; 685 } else { 686 Log.w(TAG, "<manifest> has more than one <application>"); 687 XmlUtils.skipCurrentTag(parser); 688 continue; 689 } 690 } 691 692 foundApp = true; 693 if (!parseApplication(pkg, res, parser, attrs, flags, outError)) { 694 return null; 695 } 696 } else if (tagName.equals("permission-group")) { 697 if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) { 698 return null; 699 } 700 } else if (tagName.equals("permission")) { 701 if (parsePermission(pkg, res, parser, attrs, outError) == null) { 702 return null; 703 } 704 } else if (tagName.equals("permission-tree")) { 705 if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) { 706 return null; 707 } 708 } else if (tagName.equals("uses-permission")) { 709 sa = res.obtainAttributes(attrs, 710 com.android.internal.R.styleable.AndroidManifestUsesPermission); 711 712 String name = sa.getNonResourceString( 713 com.android.internal.R.styleable.AndroidManifestUsesPermission_name); 714 715 sa.recycle(); 716 717 if (name != null && !pkg.requestedPermissions.contains(name)) { 718 pkg.requestedPermissions.add(name); 719 } 720 721 XmlUtils.skipCurrentTag(parser); 722 723 } else if (tagName.equals("uses-configuration")) { 724 ConfigurationInfo cPref = new ConfigurationInfo(); 725 sa = res.obtainAttributes(attrs, 726 com.android.internal.R.styleable.AndroidManifestUsesConfiguration); 727 cPref.reqTouchScreen = sa.getInt( 728 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen, 729 Configuration.TOUCHSCREEN_UNDEFINED); 730 cPref.reqKeyboardType = sa.getInt( 731 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType, 732 Configuration.KEYBOARD_UNDEFINED); 733 if (sa.getBoolean( 734 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard, 735 false)) { 736 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD; 737 } 738 cPref.reqNavigation = sa.getInt( 739 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation, 740 Configuration.NAVIGATION_UNDEFINED); 741 if (sa.getBoolean( 742 com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav, 743 false)) { 744 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV; 745 } 746 sa.recycle(); 747 pkg.configPreferences.add(cPref); 748 749 XmlUtils.skipCurrentTag(parser); 750 751 } else if (tagName.equals("uses-sdk")) { 752 if (mSdkVersion > 0) { 753 sa = res.obtainAttributes(attrs, 754 com.android.internal.R.styleable.AndroidManifestUsesSdk); 755 756 int minVers = 0; 757 String minCode = null; 758 int targetVers = 0; 759 String targetCode = null; 760 761 TypedValue val = sa.peekValue( 762 com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion); 763 if (val != null) { 764 if (val.type == TypedValue.TYPE_STRING && val.string != null) { 765 targetCode = minCode = val.string.toString(); 766 } else { 767 // If it's not a string, it's an integer. 768 minVers = val.data; 769 } 770 } 771 772 val = sa.peekValue( 773 com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion); 774 if (val != null) { 775 if (val.type == TypedValue.TYPE_STRING && val.string != null) { 776 targetCode = minCode = val.string.toString(); 777 } else { 778 // If it's not a string, it's an integer. 779 targetVers = val.data; 780 } 781 } 782 783 int maxVers = sa.getInt( 784 com.android.internal.R.styleable.AndroidManifestUsesSdk_maxSdkVersion, 785 mSdkVersion); 786 787 sa.recycle(); 788 789 if (targetCode != null) { 790 if (!targetCode.equals(mSdkCodename)) { 791 if (mSdkCodename != null) { 792 outError[0] = "Requires development platform " + targetCode 793 + " (current platform is " + mSdkCodename + ")"; 794 } else { 795 outError[0] = "Requires development platform " + targetCode 796 + " but this is a release platform."; 797 } 798 mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; 799 return null; 800 } 801 // If the code matches, it definitely targets this SDK. 802 pkg.applicationInfo.targetSdkVersion 803 = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; 804 } else { 805 pkg.applicationInfo.targetSdkVersion = targetVers; 806 } 807 808 if (minVers > mSdkVersion) { 809 outError[0] = "Requires newer sdk version #" + minVers 810 + " (current version is #" + mSdkVersion + ")"; 811 mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; 812 return null; 813 } 814 815 if (maxVers < mSdkVersion) { 816 outError[0] = "Requires older sdk version #" + maxVers 817 + " (current version is #" + mSdkVersion + ")"; 818 mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; 819 return null; 820 } 821 } 822 823 XmlUtils.skipCurrentTag(parser); 824 825 } else if (tagName.equals("instrumentation")) { 826 if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) { 827 return null; 828 } 829 } else if (tagName.equals("eat-comment")) { 830 // Just skip this tag 831 XmlUtils.skipCurrentTag(parser); 832 continue; 833 } else if (RIGID_PARSER) { 834 outError[0] = "Bad element under <manifest>: " 835 + parser.getName(); 836 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 837 return null; 838 } else { 839 Log.w(TAG, "Bad element under <manifest>: " 840 + parser.getName()); 841 XmlUtils.skipCurrentTag(parser); 842 continue; 843 } 844 } 845 846 if (!foundApp && pkg.instrumentation.size() == 0) { 847 outError[0] = "<manifest> does not contain an <application> or <instrumentation>"; 848 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY; 849 } 850 851 final int NP = PackageParser.NEW_PERMISSIONS.length; 852 for (int ip=0; ip<NP; ip++) { 853 final PackageParser.NewPermissionInfo npi 854 = PackageParser.NEW_PERMISSIONS[ip]; 855 if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) { 856 break; 857 } 858 if (!pkg.requestedPermissions.contains(npi.name)) { 859 Log.i(TAG, "Impliciting adding " + npi.name + " to old pkg " 860 + pkg.packageName); 861 pkg.requestedPermissions.add(npi.name); 862 } 863 } 864 865 if (pkg.usesLibraries.size() > 0) { 866 pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()]; 867 pkg.usesLibraries.toArray(pkg.usesLibraryFiles); 868 } 869 870 int size = pkg.supportsDensityList.size(); 871 if (size > 0) { 872 int densities[] = pkg.supportsDensities = new int[size]; 873 List<Integer> densityList = pkg.supportsDensityList; 874 for (int i = 0; i < size; i++) { 875 densities[i] = densityList.get(i); 876 } 877 } 878 return pkg; 879 } 880 881 private static String buildClassName(String pkg, CharSequence clsSeq, 882 String[] outError) { 883 if (clsSeq == null || clsSeq.length() <= 0) { 884 outError[0] = "Empty class name in package " + pkg; 885 return null; 886 } 887 String cls = clsSeq.toString(); 888 char c = cls.charAt(0); 889 if (c == '.') { 890 return (pkg + cls).intern(); 891 } 892 if (cls.indexOf('.') < 0) { 893 StringBuilder b = new StringBuilder(pkg); 894 b.append('.'); 895 b.append(cls); 896 return b.toString().intern(); 897 } 898 if (c >= 'a' && c <= 'z') { 899 return cls.intern(); 900 } 901 outError[0] = "Bad class name " + cls + " in package " + pkg; 902 return null; 903 } 904 905 private static String buildCompoundName(String pkg, 906 CharSequence procSeq, String type, String[] outError) { 907 String proc = procSeq.toString(); 908 char c = proc.charAt(0); 909 if (pkg != null && c == ':') { 910 if (proc.length() < 2) { 911 outError[0] = "Bad " + type + " name " + proc + " in package " + pkg 912 + ": must be at least two characters"; 913 return null; 914 } 915 String subName = proc.substring(1); 916 String nameError = validateName(subName, false); 917 if (nameError != null) { 918 outError[0] = "Invalid " + type + " name " + proc + " in package " 919 + pkg + ": " + nameError; 920 return null; 921 } 922 return (pkg + proc).intern(); 923 } 924 String nameError = validateName(proc, true); 925 if (nameError != null && !"system".equals(proc)) { 926 outError[0] = "Invalid " + type + " name " + proc + " in package " 927 + pkg + ": " + nameError; 928 return null; 929 } 930 return proc.intern(); 931 } 932 933 private static String buildProcessName(String pkg, String defProc, 934 CharSequence procSeq, int flags, String[] separateProcesses, 935 String[] outError) { 936 if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) { 937 return defProc != null ? defProc : pkg; 938 } 939 if (separateProcesses != null) { 940 for (int i=separateProcesses.length-1; i>=0; i--) { 941 String sp = separateProcesses[i]; 942 if (sp.equals(pkg) || sp.equals(defProc) || sp.equals(procSeq)) { 943 return pkg; 944 } 945 } 946 } 947 if (procSeq == null || procSeq.length() <= 0) { 948 return defProc; 949 } 950 return buildCompoundName(pkg, procSeq, "package", outError); 951 } 952 953 private static String buildTaskAffinityName(String pkg, String defProc, 954 CharSequence procSeq, String[] outError) { 955 if (procSeq == null) { 956 return defProc; 957 } 958 if (procSeq.length() <= 0) { 959 return null; 960 } 961 return buildCompoundName(pkg, procSeq, "taskAffinity", outError); 962 } 963 964 private PermissionGroup parsePermissionGroup(Package owner, Resources res, 965 XmlPullParser parser, AttributeSet attrs, String[] outError) 966 throws XmlPullParserException, IOException { 967 PermissionGroup perm = new PermissionGroup(owner); 968 969 TypedArray sa = res.obtainAttributes(attrs, 970 com.android.internal.R.styleable.AndroidManifestPermissionGroup); 971 972 if (!parsePackageItemInfo(owner, perm.info, outError, 973 "<permission-group>", sa, 974 com.android.internal.R.styleable.AndroidManifestPermissionGroup_name, 975 com.android.internal.R.styleable.AndroidManifestPermissionGroup_label, 976 com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon)) { 977 sa.recycle(); 978 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 979 return null; 980 } 981 982 perm.info.descriptionRes = sa.getResourceId( 983 com.android.internal.R.styleable.AndroidManifestPermissionGroup_description, 984 0); 985 986 sa.recycle(); 987 988 if (!parseAllMetaData(res, parser, attrs, "<permission-group>", perm, 989 outError)) { 990 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 991 return null; 992 } 993 994 owner.permissionGroups.add(perm); 995 996 return perm; 997 } 998 999 private Permission parsePermission(Package owner, Resources res, 1000 XmlPullParser parser, AttributeSet attrs, String[] outError) 1001 throws XmlPullParserException, IOException { 1002 Permission perm = new Permission(owner); 1003 1004 TypedArray sa = res.obtainAttributes(attrs, 1005 com.android.internal.R.styleable.AndroidManifestPermission); 1006 1007 if (!parsePackageItemInfo(owner, perm.info, outError, 1008 "<permission>", sa, 1009 com.android.internal.R.styleable.AndroidManifestPermission_name, 1010 com.android.internal.R.styleable.AndroidManifestPermission_label, 1011 com.android.internal.R.styleable.AndroidManifestPermission_icon)) { 1012 sa.recycle(); 1013 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1014 return null; 1015 } 1016 1017 perm.info.group = sa.getNonResourceString( 1018 com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup); 1019 if (perm.info.group != null) { 1020 perm.info.group = perm.info.group.intern(); 1021 } 1022 1023 perm.info.descriptionRes = sa.getResourceId( 1024 com.android.internal.R.styleable.AndroidManifestPermission_description, 1025 0); 1026 1027 perm.info.protectionLevel = sa.getInt( 1028 com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel, 1029 PermissionInfo.PROTECTION_NORMAL); 1030 1031 sa.recycle(); 1032 1033 if (perm.info.protectionLevel == -1) { 1034 outError[0] = "<permission> does not specify protectionLevel"; 1035 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1036 return null; 1037 } 1038 1039 if (!parseAllMetaData(res, parser, attrs, "<permission>", perm, 1040 outError)) { 1041 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1042 return null; 1043 } 1044 1045 owner.permissions.add(perm); 1046 1047 return perm; 1048 } 1049 1050 private Permission parsePermissionTree(Package owner, Resources res, 1051 XmlPullParser parser, AttributeSet attrs, String[] outError) 1052 throws XmlPullParserException, IOException { 1053 Permission perm = new Permission(owner); 1054 1055 TypedArray sa = res.obtainAttributes(attrs, 1056 com.android.internal.R.styleable.AndroidManifestPermissionTree); 1057 1058 if (!parsePackageItemInfo(owner, perm.info, outError, 1059 "<permission-tree>", sa, 1060 com.android.internal.R.styleable.AndroidManifestPermissionTree_name, 1061 com.android.internal.R.styleable.AndroidManifestPermissionTree_label, 1062 com.android.internal.R.styleable.AndroidManifestPermissionTree_icon)) { 1063 sa.recycle(); 1064 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1065 return null; 1066 } 1067 1068 sa.recycle(); 1069 1070 int index = perm.info.name.indexOf('.'); 1071 if (index > 0) { 1072 index = perm.info.name.indexOf('.', index+1); 1073 } 1074 if (index < 0) { 1075 outError[0] = "<permission-tree> name has less than three segments: " 1076 + perm.info.name; 1077 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1078 return null; 1079 } 1080 1081 perm.info.descriptionRes = 0; 1082 perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL; 1083 perm.tree = true; 1084 1085 if (!parseAllMetaData(res, parser, attrs, "<permission-tree>", perm, 1086 outError)) { 1087 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1088 return null; 1089 } 1090 1091 owner.permissions.add(perm); 1092 1093 return perm; 1094 } 1095 1096 private Instrumentation parseInstrumentation(Package owner, Resources res, 1097 XmlPullParser parser, AttributeSet attrs, String[] outError) 1098 throws XmlPullParserException, IOException { 1099 TypedArray sa = res.obtainAttributes(attrs, 1100 com.android.internal.R.styleable.AndroidManifestInstrumentation); 1101 1102 if (mParseInstrumentationArgs == null) { 1103 mParseInstrumentationArgs = new ParsePackageItemArgs(owner, outError, 1104 com.android.internal.R.styleable.AndroidManifestInstrumentation_name, 1105 com.android.internal.R.styleable.AndroidManifestInstrumentation_label, 1106 com.android.internal.R.styleable.AndroidManifestInstrumentation_icon); 1107 mParseInstrumentationArgs.tag = "<instrumentation>"; 1108 } 1109 1110 mParseInstrumentationArgs.sa = sa; 1111 1112 Instrumentation a = new Instrumentation(mParseInstrumentationArgs, 1113 new InstrumentationInfo()); 1114 if (outError[0] != null) { 1115 sa.recycle(); 1116 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1117 return null; 1118 } 1119 1120 String str; 1121 str = sa.getNonResourceString( 1122 com.android.internal.R.styleable.AndroidManifestInstrumentation_targetPackage); 1123 a.info.targetPackage = str != null ? str.intern() : null; 1124 1125 a.info.handleProfiling = sa.getBoolean( 1126 com.android.internal.R.styleable.AndroidManifestInstrumentation_handleProfiling, 1127 false); 1128 1129 a.info.functionalTest = sa.getBoolean( 1130 com.android.internal.R.styleable.AndroidManifestInstrumentation_functionalTest, 1131 false); 1132 1133 sa.recycle(); 1134 1135 if (a.info.targetPackage == null) { 1136 outError[0] = "<instrumentation> does not specify targetPackage"; 1137 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1138 return null; 1139 } 1140 1141 if (!parseAllMetaData(res, parser, attrs, "<instrumentation>", a, 1142 outError)) { 1143 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1144 return null; 1145 } 1146 1147 owner.instrumentation.add(a); 1148 1149 return a; 1150 } 1151 1152 private boolean parseApplication(Package owner, Resources res, 1153 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) 1154 throws XmlPullParserException, IOException { 1155 final ApplicationInfo ai = owner.applicationInfo; 1156 final String pkgName = owner.applicationInfo.packageName; 1157 1158 TypedArray sa = res.obtainAttributes(attrs, 1159 com.android.internal.R.styleable.AndroidManifestApplication); 1160 1161 String name = sa.getNonResourceString( 1162 com.android.internal.R.styleable.AndroidManifestApplication_name); 1163 if (name != null) { 1164 ai.className = buildClassName(pkgName, name, outError); 1165 if (ai.className == null) { 1166 sa.recycle(); 1167 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1168 return false; 1169 } 1170 } 1171 1172 String manageSpaceActivity = sa.getNonResourceString( 1173 com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity); 1174 if (manageSpaceActivity != null) { 1175 ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity, 1176 outError); 1177 } 1178 1179 TypedValue v = sa.peekValue( 1180 com.android.internal.R.styleable.AndroidManifestApplication_label); 1181 if (v != null && (ai.labelRes=v.resourceId) == 0) { 1182 ai.nonLocalizedLabel = v.coerceToString(); 1183 } 1184 1185 ai.icon = sa.getResourceId( 1186 com.android.internal.R.styleable.AndroidManifestApplication_icon, 0); 1187 ai.theme = sa.getResourceId( 1188 com.android.internal.R.styleable.AndroidManifestApplication_theme, 0); 1189 ai.descriptionRes = sa.getResourceId( 1190 com.android.internal.R.styleable.AndroidManifestApplication_description, 0); 1191 1192 if ((flags&PARSE_IS_SYSTEM) != 0) { 1193 if (sa.getBoolean( 1194 com.android.internal.R.styleable.AndroidManifestApplication_persistent, 1195 false)) { 1196 ai.flags |= ApplicationInfo.FLAG_PERSISTENT; 1197 } 1198 } 1199 1200 if (sa.getBoolean( 1201 com.android.internal.R.styleable.AndroidManifestApplication_debuggable, 1202 false)) { 1203 ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE; 1204 } 1205 1206 if (sa.getBoolean( 1207 com.android.internal.R.styleable.AndroidManifestApplication_hasCode, 1208 true)) { 1209 ai.flags |= ApplicationInfo.FLAG_HAS_CODE; 1210 } 1211 1212 if (sa.getBoolean( 1213 com.android.internal.R.styleable.AndroidManifestApplication_allowTaskReparenting, 1214 false)) { 1215 ai.flags |= ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING; 1216 } 1217 1218 if (sa.getBoolean( 1219 com.android.internal.R.styleable.AndroidManifestApplication_allowClearUserData, 1220 true)) { 1221 ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA; 1222 } 1223 1224 if (sa.getBoolean( 1225 com.android.internal.R.styleable.AndroidManifestApplication_testOnly, 1226 false)) { 1227 ai.flags |= ApplicationInfo.FLAG_TEST_ONLY; 1228 } 1229 1230 String str; 1231 str = sa.getNonResourceString( 1232 com.android.internal.R.styleable.AndroidManifestApplication_permission); 1233 ai.permission = (str != null && str.length() > 0) ? str.intern() : null; 1234 1235 str = sa.getNonResourceString( 1236 com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity); 1237 ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName, 1238 str, outError); 1239 1240 if (outError[0] == null) { 1241 ai.processName = buildProcessName(ai.packageName, null, sa.getNonResourceString( 1242 com.android.internal.R.styleable.AndroidManifestApplication_process), 1243 flags, mSeparateProcesses, outError); 1244 1245 ai.enabled = sa.getBoolean(com.android.internal.R.styleable.AndroidManifestApplication_enabled, true); 1246 } 1247 1248 sa.recycle(); 1249 1250 if (outError[0] != null) { 1251 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1252 return false; 1253 } 1254 1255 final int innerDepth = parser.getDepth(); 1256 1257 int type; 1258 while ((type=parser.next()) != parser.END_DOCUMENT 1259 && (type != parser.END_TAG || parser.getDepth() > innerDepth)) { 1260 if (type == parser.END_TAG || type == parser.TEXT) { 1261 continue; 1262 } 1263 1264 String tagName = parser.getName(); 1265 if (tagName.equals("activity")) { 1266 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false); 1267 if (a == null) { 1268 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1269 return false; 1270 } 1271 1272 owner.activities.add(a); 1273 1274 } else if (tagName.equals("receiver")) { 1275 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true); 1276 if (a == null) { 1277 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1278 return false; 1279 } 1280 1281 owner.receivers.add(a); 1282 1283 } else if (tagName.equals("service")) { 1284 Service s = parseService(owner, res, parser, attrs, flags, outError); 1285 if (s == null) { 1286 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1287 return false; 1288 } 1289 1290 owner.services.add(s); 1291 1292 } else if (tagName.equals("provider")) { 1293 Provider p = parseProvider(owner, res, parser, attrs, flags, outError); 1294 if (p == null) { 1295 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1296 return false; 1297 } 1298 1299 owner.providers.add(p); 1300 1301 } else if (tagName.equals("activity-alias")) { 1302 Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError); 1303 if (a == null) { 1304 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1305 return false; 1306 } 1307 1308 owner.activities.add(a); 1309 1310 } else if (parser.getName().equals("meta-data")) { 1311 // note: application meta-data is stored off to the side, so it can 1312 // remain null in the primary copy (we like to avoid extra copies because 1313 // it can be large) 1314 if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData, 1315 outError)) == null) { 1316 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1317 return false; 1318 } 1319 1320 } else if (tagName.equals("uses-library")) { 1321 sa = res.obtainAttributes(attrs, 1322 com.android.internal.R.styleable.AndroidManifestUsesLibrary); 1323 1324 String lname = sa.getNonResourceString( 1325 com.android.internal.R.styleable.AndroidManifestUsesLibrary_name); 1326 1327 sa.recycle(); 1328 1329 if (lname != null && !owner.usesLibraries.contains(lname)) { 1330 owner.usesLibraries.add(lname); 1331 } 1332 1333 XmlUtils.skipCurrentTag(parser); 1334 1335 } else if (tagName.equals("supports-density")) { 1336 sa = res.obtainAttributes(attrs, 1337 com.android.internal.R.styleable.AndroidManifestSupportsDensity); 1338 1339 int density = sa.getInteger( 1340 com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1); 1341 1342 sa.recycle(); 1343 1344 if (density != -1 && !owner.supportsDensityList.contains(density)) { 1345 owner.supportsDensityList.add(density); 1346 } 1347 1348 XmlUtils.skipCurrentTag(parser); 1349 1350 } else { 1351 if (!RIGID_PARSER) { 1352 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); 1353 Log.w(TAG, "Unknown element under <application>: " + tagName); 1354 XmlUtils.skipCurrentTag(parser); 1355 continue; 1356 } else { 1357 outError[0] = "Bad element under <application>: " + tagName; 1358 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 1359 return false; 1360 } 1361 } 1362 } 1363 1364 return true; 1365 } 1366 1367 private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo, 1368 String[] outError, String tag, TypedArray sa, 1369 int nameRes, int labelRes, int iconRes) { 1370 String name = sa.getNonResourceString(nameRes); 1371 if (name == null) { 1372 outError[0] = tag + " does not specify android:name"; 1373 return false; 1374 } 1375 1376 outInfo.name 1377 = buildClassName(owner.applicationInfo.packageName, name, outError); 1378 if (outInfo.name == null) { 1379 return false; 1380 } 1381 1382 int iconVal = sa.getResourceId(iconRes, 0); 1383 if (iconVal != 0) { 1384 outInfo.icon = iconVal; 1385 outInfo.nonLocalizedLabel = null; 1386 } 1387 1388 TypedValue v = sa.peekValue(labelRes); 1389 if (v != null && (outInfo.labelRes=v.resourceId) == 0) { 1390 outInfo.nonLocalizedLabel = v.coerceToString(); 1391 } 1392 1393 outInfo.packageName = owner.packageName; 1394 1395 return true; 1396 } 1397 1398 private boolean parseComponentInfo(Package owner, int flags, 1399 ComponentInfo outInfo, String[] outError, String tag, TypedArray sa, 1400 int nameRes, int labelRes, int iconRes, int processRes, 1401 int enabledRes) { 1402 if (!parsePackageItemInfo(owner, outInfo, outError, tag, sa, 1403 nameRes, labelRes, iconRes)) { 1404 return false; 1405 } 1406 1407 if (processRes != 0) { 1408 outInfo.processName = buildProcessName(owner.applicationInfo.packageName, 1409 owner.applicationInfo.processName, sa.getNonResourceString(processRes), 1410 flags, mSeparateProcesses, outError); 1411 } 1412 outInfo.enabled = sa.getBoolean(enabledRes, true); 1413 1414 return outError[0] == null; 1415 } 1416 1417 private Activity parseActivity(Package owner, Resources res, 1418 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError, 1419 boolean receiver) throws XmlPullParserException, IOException { 1420 TypedArray sa = res.obtainAttributes(attrs, 1421 com.android.internal.R.styleable.AndroidManifestActivity); 1422 1423 if (mParseActivityArgs == null) { 1424 mParseActivityArgs = new ParseComponentArgs(owner, outError, 1425 com.android.internal.R.styleable.AndroidManifestActivity_name, 1426 com.android.internal.R.styleable.AndroidManifestActivity_label, 1427 com.android.internal.R.styleable.AndroidManifestActivity_icon, 1428 mSeparateProcesses, 1429 com.android.internal.R.styleable.AndroidManifestActivity_process, 1430 com.android.internal.R.styleable.AndroidManifestActivity_enabled); 1431 } 1432 1433 mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>"; 1434 mParseActivityArgs.sa = sa; 1435 mParseActivityArgs.flags = flags; 1436 1437 Activity a = new Activity(mParseActivityArgs, new ActivityInfo()); 1438 if (outError[0] != null) { 1439 sa.recycle(); 1440 return null; 1441 } 1442 1443 final boolean setExported = sa.hasValue( 1444 com.android.internal.R.styleable.AndroidManifestActivity_exported); 1445 if (setExported) { 1446 a.info.exported = sa.getBoolean( 1447 com.android.internal.R.styleable.AndroidManifestActivity_exported, false); 1448 } 1449 1450 a.info.theme = sa.getResourceId( 1451 com.android.internal.R.styleable.AndroidManifestActivity_theme, 0); 1452 1453 String str; 1454 str = sa.getNonResourceString( 1455 com.android.internal.R.styleable.AndroidManifestActivity_permission); 1456 if (str == null) { 1457 a.info.permission = owner.applicationInfo.permission; 1458 } else { 1459 a.info.permission = str.length() > 0 ? str.toString().intern() : null; 1460 } 1461 1462 str = sa.getNonResourceString( 1463 com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity); 1464 a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName, 1465 owner.applicationInfo.taskAffinity, str, outError); 1466 1467 a.info.flags = 0; 1468 if (sa.getBoolean( 1469 com.android.internal.R.styleable.AndroidManifestActivity_multiprocess, 1470 false)) { 1471 a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS; 1472 } 1473 1474 if (sa.getBoolean( 1475 com.android.internal.R.styleable.AndroidManifestActivity_finishOnTaskLaunch, 1476 false)) { 1477 a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH; 1478 } 1479 1480 if (sa.getBoolean( 1481 com.android.internal.R.styleable.AndroidManifestActivity_clearTaskOnLaunch, 1482 false)) { 1483 a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH; 1484 } 1485 1486 if (sa.getBoolean( 1487 com.android.internal.R.styleable.AndroidManifestActivity_noHistory, 1488 false)) { 1489 a.info.flags |= ActivityInfo.FLAG_NO_HISTORY; 1490 } 1491 1492 if (sa.getBoolean( 1493 com.android.internal.R.styleable.AndroidManifestActivity_alwaysRetainTaskState, 1494 false)) { 1495 a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE; 1496 } 1497 1498 if (sa.getBoolean( 1499 com.android.internal.R.styleable.AndroidManifestActivity_stateNotNeeded, 1500 false)) { 1501 a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED; 1502 } 1503 1504 if (sa.getBoolean( 1505 com.android.internal.R.styleable.AndroidManifestActivity_excludeFromRecents, 1506 false)) { 1507 a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; 1508 } 1509 1510 if (sa.getBoolean( 1511 com.android.internal.R.styleable.AndroidManifestActivity_allowTaskReparenting, 1512 (owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) { 1513 a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING; 1514 } 1515 1516 if (!receiver) { 1517 a.info.launchMode = sa.getInt( 1518 com.android.internal.R.styleable.AndroidManifestActivity_launchMode, 1519 ActivityInfo.LAUNCH_MULTIPLE); 1520 a.info.screenOrientation = sa.getInt( 1521 com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation, 1522 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 1523 a.info.configChanges = sa.getInt( 1524 com.android.internal.R.styleable.AndroidManifestActivity_configChanges, 1525 0); 1526 a.info.softInputMode = sa.getInt( 1527 com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode, 1528 0); 1529 } else { 1530 a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE; 1531 a.info.configChanges = 0; 1532 } 1533 1534 sa.recycle(); 1535 1536 if (outError[0] != null) { 1537 return null; 1538 } 1539 1540 int outerDepth = parser.getDepth(); 1541 int type; 1542 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1543 && (type != XmlPullParser.END_TAG 1544 || parser.getDepth() > outerDepth)) { 1545 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1546 continue; 1547 } 1548 1549 if (parser.getName().equals("intent-filter")) { 1550 ActivityIntentInfo intent = new ActivityIntentInfo(a); 1551 if (!parseIntent(res, parser, attrs, flags, intent, outError, !receiver)) { 1552 return null; 1553 } 1554 if (intent.countActions() == 0) { 1555 Log.w(TAG, "Intent filter for activity " + intent 1556 + " defines no actions"); 1557 } else { 1558 a.intents.add(intent); 1559 } 1560 } else if (parser.getName().equals("meta-data")) { 1561 if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData, 1562 outError)) == null) { 1563 return null; 1564 } 1565 } else { 1566 if (!RIGID_PARSER) { 1567 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); 1568 if (receiver) { 1569 Log.w(TAG, "Unknown element under <receiver>: " + parser.getName()); 1570 } else { 1571 Log.w(TAG, "Unknown element under <activity>: " + parser.getName()); 1572 } 1573 XmlUtils.skipCurrentTag(parser); 1574 continue; 1575 } 1576 if (receiver) { 1577 outError[0] = "Bad element under <receiver>: " + parser.getName(); 1578 } else { 1579 outError[0] = "Bad element under <activity>: " + parser.getName(); 1580 } 1581 return null; 1582 } 1583 } 1584 1585 if (!setExported) { 1586 a.info.exported = a.intents.size() > 0; 1587 } 1588 1589 return a; 1590 } 1591 1592 private Activity parseActivityAlias(Package owner, Resources res, 1593 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) 1594 throws XmlPullParserException, IOException { 1595 TypedArray sa = res.obtainAttributes(attrs, 1596 com.android.internal.R.styleable.AndroidManifestActivityAlias); 1597 1598 String targetActivity = sa.getNonResourceString( 1599 com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity); 1600 if (targetActivity == null) { 1601 outError[0] = "<activity-alias> does not specify android:targetActivity"; 1602 sa.recycle(); 1603 return null; 1604 } 1605 1606 targetActivity = buildClassName(owner.applicationInfo.packageName, 1607 targetActivity, outError); 1608 if (targetActivity == null) { 1609 sa.recycle(); 1610 return null; 1611 } 1612 1613 if (mParseActivityAliasArgs == null) { 1614 mParseActivityAliasArgs = new ParseComponentArgs(owner, outError, 1615 com.android.internal.R.styleable.AndroidManifestActivityAlias_name, 1616 com.android.internal.R.styleable.AndroidManifestActivityAlias_label, 1617 com.android.internal.R.styleable.AndroidManifestActivityAlias_icon, 1618 mSeparateProcesses, 1619 0, 1620 com.android.internal.R.styleable.AndroidManifestActivityAlias_enabled); 1621 mParseActivityAliasArgs.tag = "<activity-alias>"; 1622 } 1623 1624 mParseActivityAliasArgs.sa = sa; 1625 mParseActivityAliasArgs.flags = flags; 1626 1627 Activity target = null; 1628 1629 final int NA = owner.activities.size(); 1630 for (int i=0; i<NA; i++) { 1631 Activity t = owner.activities.get(i); 1632 if (targetActivity.equals(t.info.name)) { 1633 target = t; 1634 break; 1635 } 1636 } 1637 1638 if (target == null) { 1639 outError[0] = "<activity-alias> target activity " + targetActivity 1640 + " not found in manifest"; 1641 sa.recycle(); 1642 return null; 1643 } 1644 1645 ActivityInfo info = new ActivityInfo(); 1646 info.targetActivity = targetActivity; 1647 info.configChanges = target.info.configChanges; 1648 info.flags = target.info.flags; 1649 info.icon = target.info.icon; 1650 info.labelRes = target.info.labelRes; 1651 info.nonLocalizedLabel = target.info.nonLocalizedLabel; 1652 info.launchMode = target.info.launchMode; 1653 info.processName = target.info.processName; 1654 info.screenOrientation = target.info.screenOrientation; 1655 info.taskAffinity = target.info.taskAffinity; 1656 info.theme = target.info.theme; 1657 1658 Activity a = new Activity(mParseActivityAliasArgs, info); 1659 if (outError[0] != null) { 1660 sa.recycle(); 1661 return null; 1662 } 1663 1664 final boolean setExported = sa.hasValue( 1665 com.android.internal.R.styleable.AndroidManifestActivityAlias_exported); 1666 if (setExported) { 1667 a.info.exported = sa.getBoolean( 1668 com.android.internal.R.styleable.AndroidManifestActivityAlias_exported, false); 1669 } 1670 1671 String str; 1672 str = sa.getNonResourceString( 1673 com.android.internal.R.styleable.AndroidManifestActivityAlias_permission); 1674 if (str != null) { 1675 a.info.permission = str.length() > 0 ? str.toString().intern() : null; 1676 } 1677 1678 sa.recycle(); 1679 1680 if (outError[0] != null) { 1681 return null; 1682 } 1683 1684 int outerDepth = parser.getDepth(); 1685 int type; 1686 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1687 && (type != XmlPullParser.END_TAG 1688 || parser.getDepth() > outerDepth)) { 1689 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1690 continue; 1691 } 1692 1693 if (parser.getName().equals("intent-filter")) { 1694 ActivityIntentInfo intent = new ActivityIntentInfo(a); 1695 if (!parseIntent(res, parser, attrs, flags, intent, outError, true)) { 1696 return null; 1697 } 1698 if (intent.countActions() == 0) { 1699 Log.w(TAG, "Intent filter for activity alias " + intent 1700 + " defines no actions"); 1701 } else { 1702 a.intents.add(intent); 1703 } 1704 } else if (parser.getName().equals("meta-data")) { 1705 if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData, 1706 outError)) == null) { 1707 return null; 1708 } 1709 } else { 1710 if (!RIGID_PARSER) { 1711 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); 1712 Log.w(TAG, "Unknown element under <activity-alias>: " + parser.getName()); 1713 XmlUtils.skipCurrentTag(parser); 1714 continue; 1715 } 1716 outError[0] = "Bad element under <activity-alias>: " + parser.getName(); 1717 return null; 1718 } 1719 } 1720 1721 if (!setExported) { 1722 a.info.exported = a.intents.size() > 0; 1723 } 1724 1725 return a; 1726 } 1727 1728 private Provider parseProvider(Package owner, Resources res, 1729 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) 1730 throws XmlPullParserException, IOException { 1731 TypedArray sa = res.obtainAttributes(attrs, 1732 com.android.internal.R.styleable.AndroidManifestProvider); 1733 1734 if (mParseProviderArgs == null) { 1735 mParseProviderArgs = new ParseComponentArgs(owner, outError, 1736 com.android.internal.R.styleable.AndroidManifestProvider_name, 1737 com.android.internal.R.styleable.AndroidManifestProvider_label, 1738 com.android.internal.R.styleable.AndroidManifestProvider_icon, 1739 mSeparateProcesses, 1740 com.android.internal.R.styleable.AndroidManifestProvider_process, 1741 com.android.internal.R.styleable.AndroidManifestProvider_enabled); 1742 mParseProviderArgs.tag = "<provider>"; 1743 } 1744 1745 mParseProviderArgs.sa = sa; 1746 mParseProviderArgs.flags = flags; 1747 1748 Provider p = new Provider(mParseProviderArgs, new ProviderInfo()); 1749 if (outError[0] != null) { 1750 sa.recycle(); 1751 return null; 1752 } 1753 1754 p.info.exported = sa.getBoolean( 1755 com.android.internal.R.styleable.AndroidManifestProvider_exported, true); 1756 1757 String cpname = sa.getNonResourceString( 1758 com.android.internal.R.styleable.AndroidManifestProvider_authorities); 1759 1760 p.info.isSyncable = sa.getBoolean( 1761 com.android.internal.R.styleable.AndroidManifestProvider_syncable, 1762 false); 1763 1764 String permission = sa.getNonResourceString( 1765 com.android.internal.R.styleable.AndroidManifestProvider_permission); 1766 String str = sa.getNonResourceString( 1767 com.android.internal.R.styleable.AndroidManifestProvider_readPermission); 1768 if (str == null) { 1769 str = permission; 1770 } 1771 if (str == null) { 1772 p.info.readPermission = owner.applicationInfo.permission; 1773 } else { 1774 p.info.readPermission = 1775 str.length() > 0 ? str.toString().intern() : null; 1776 } 1777 str = sa.getNonResourceString( 1778 com.android.internal.R.styleable.AndroidManifestProvider_writePermission); 1779 if (str == null) { 1780 str = permission; 1781 } 1782 if (str == null) { 1783 p.info.writePermission = owner.applicationInfo.permission; 1784 } else { 1785 p.info.writePermission = 1786 str.length() > 0 ? str.toString().intern() : null; 1787 } 1788 1789 p.info.grantUriPermissions = sa.getBoolean( 1790 com.android.internal.R.styleable.AndroidManifestProvider_grantUriPermissions, 1791 false); 1792 1793 p.info.multiprocess = sa.getBoolean( 1794 com.android.internal.R.styleable.AndroidManifestProvider_multiprocess, 1795 false); 1796 1797 p.info.initOrder = sa.getInt( 1798 com.android.internal.R.styleable.AndroidManifestProvider_initOrder, 1799 0); 1800 1801 sa.recycle(); 1802 1803 if (cpname == null) { 1804 outError[0] = "<provider> does not incude authorities attribute"; 1805 return null; 1806 } 1807 p.info.authority = cpname.intern(); 1808 1809 if (!parseProviderTags(res, parser, attrs, p, outError)) { 1810 return null; 1811 } 1812 1813 return p; 1814 } 1815 1816 private boolean parseProviderTags(Resources res, 1817 XmlPullParser parser, AttributeSet attrs, 1818 Provider outInfo, String[] outError) 1819 throws XmlPullParserException, IOException { 1820 int outerDepth = parser.getDepth(); 1821 int type; 1822 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1823 && (type != XmlPullParser.END_TAG 1824 || parser.getDepth() > outerDepth)) { 1825 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1826 continue; 1827 } 1828 1829 if (parser.getName().equals("meta-data")) { 1830 if ((outInfo.metaData=parseMetaData(res, parser, attrs, 1831 outInfo.metaData, outError)) == null) { 1832 return false; 1833 } 1834 } else if (parser.getName().equals("grant-uri-permission")) { 1835 TypedArray sa = res.obtainAttributes(attrs, 1836 com.android.internal.R.styleable.AndroidManifestGrantUriPermission); 1837 1838 PatternMatcher pa = null; 1839 1840 String str = sa.getNonResourceString( 1841 com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path); 1842 if (str != null) { 1843 pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL); 1844 } 1845 1846 str = sa.getNonResourceString( 1847 com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPrefix); 1848 if (str != null) { 1849 pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX); 1850 } 1851 1852 str = sa.getNonResourceString( 1853 com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPattern); 1854 if (str != null) { 1855 pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB); 1856 } 1857 1858 sa.recycle(); 1859 1860 if (pa != null) { 1861 if (outInfo.info.uriPermissionPatterns == null) { 1862 outInfo.info.uriPermissionPatterns = new PatternMatcher[1]; 1863 outInfo.info.uriPermissionPatterns[0] = pa; 1864 } else { 1865 final int N = outInfo.info.uriPermissionPatterns.length; 1866 PatternMatcher[] newp = new PatternMatcher[N+1]; 1867 System.arraycopy(outInfo.info.uriPermissionPatterns, 0, newp, 0, N); 1868 newp[N] = pa; 1869 outInfo.info.uriPermissionPatterns = newp; 1870 } 1871 outInfo.info.grantUriPermissions = true; 1872 } 1873 XmlUtils.skipCurrentTag(parser); 1874 1875 } else { 1876 if (!RIGID_PARSER) { 1877 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); 1878 Log.w(TAG, "Unknown element under <provider>: " 1879 + parser.getName()); 1880 XmlUtils.skipCurrentTag(parser); 1881 continue; 1882 } 1883 outError[0] = "Bad element under <provider>: " 1884 + parser.getName(); 1885 return false; 1886 } 1887 } 1888 return true; 1889 } 1890 1891 private Service parseService(Package owner, Resources res, 1892 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) 1893 throws XmlPullParserException, IOException { 1894 TypedArray sa = res.obtainAttributes(attrs, 1895 com.android.internal.R.styleable.AndroidManifestService); 1896 1897 if (mParseServiceArgs == null) { 1898 mParseServiceArgs = new ParseComponentArgs(owner, outError, 1899 com.android.internal.R.styleable.AndroidManifestService_name, 1900 com.android.internal.R.styleable.AndroidManifestService_label, 1901 com.android.internal.R.styleable.AndroidManifestService_icon, 1902 mSeparateProcesses, 1903 com.android.internal.R.styleable.AndroidManifestService_process, 1904 com.android.internal.R.styleable.AndroidManifestService_enabled); 1905 mParseServiceArgs.tag = "<service>"; 1906 } 1907 1908 mParseServiceArgs.sa = sa; 1909 mParseServiceArgs.flags = flags; 1910 1911 Service s = new Service(mParseServiceArgs, new ServiceInfo()); 1912 if (outError[0] != null) { 1913 sa.recycle(); 1914 return null; 1915 } 1916 1917 final boolean setExported = sa.hasValue( 1918 com.android.internal.R.styleable.AndroidManifestService_exported); 1919 if (setExported) { 1920 s.info.exported = sa.getBoolean( 1921 com.android.internal.R.styleable.AndroidManifestService_exported, false); 1922 } 1923 1924 String str = sa.getNonResourceString( 1925 com.android.internal.R.styleable.AndroidManifestService_permission); 1926 if (str == null) { 1927 s.info.permission = owner.applicationInfo.permission; 1928 } else { 1929 s.info.permission = str.length() > 0 ? str.toString().intern() : null; 1930 } 1931 1932 sa.recycle(); 1933 1934 int outerDepth = parser.getDepth(); 1935 int type; 1936 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1937 && (type != XmlPullParser.END_TAG 1938 || parser.getDepth() > outerDepth)) { 1939 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1940 continue; 1941 } 1942 1943 if (parser.getName().equals("intent-filter")) { 1944 ServiceIntentInfo intent = new ServiceIntentInfo(s); 1945 if (!parseIntent(res, parser, attrs, flags, intent, outError, false)) { 1946 return null; 1947 } 1948 1949 s.intents.add(intent); 1950 } else if (parser.getName().equals("meta-data")) { 1951 if ((s.metaData=parseMetaData(res, parser, attrs, s.metaData, 1952 outError)) == null) { 1953 return null; 1954 } 1955 } else { 1956 if (!RIGID_PARSER) { 1957 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); 1958 Log.w(TAG, "Unknown element under <service>: " 1959 + parser.getName()); 1960 XmlUtils.skipCurrentTag(parser); 1961 continue; 1962 } 1963 outError[0] = "Bad element under <service>: " 1964 + parser.getName(); 1965 return null; 1966 } 1967 } 1968 1969 if (!setExported) { 1970 s.info.exported = s.intents.size() > 0; 1971 } 1972 1973 return s; 1974 } 1975 1976 private boolean parseAllMetaData(Resources res, 1977 XmlPullParser parser, AttributeSet attrs, String tag, 1978 Component outInfo, String[] outError) 1979 throws XmlPullParserException, IOException { 1980 int outerDepth = parser.getDepth(); 1981 int type; 1982 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1983 && (type != XmlPullParser.END_TAG 1984 || parser.getDepth() > outerDepth)) { 1985 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1986 continue; 1987 } 1988 1989 if (parser.getName().equals("meta-data")) { 1990 if ((outInfo.metaData=parseMetaData(res, parser, attrs, 1991 outInfo.metaData, outError)) == null) { 1992 return false; 1993 } 1994 } else { 1995 if (!RIGID_PARSER) { 1996 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); 1997 Log.w(TAG, "Unknown element under " + tag + ": " 1998 + parser.getName()); 1999 XmlUtils.skipCurrentTag(parser); 2000 continue; 2001 } 2002 outError[0] = "Bad element under " + tag + ": " 2003 + parser.getName(); 2004 return false; 2005 } 2006 } 2007 return true; 2008 } 2009 2010 private Bundle parseMetaData(Resources res, 2011 XmlPullParser parser, AttributeSet attrs, 2012 Bundle data, String[] outError) 2013 throws XmlPullParserException, IOException { 2014 2015 TypedArray sa = res.obtainAttributes(attrs, 2016 com.android.internal.R.styleable.AndroidManifestMetaData); 2017 2018 if (data == null) { 2019 data = new Bundle(); 2020 } 2021 2022 String name = sa.getNonResourceString( 2023 com.android.internal.R.styleable.AndroidManifestMetaData_name); 2024 if (name == null) { 2025 outError[0] = "<meta-data> requires an android:name attribute"; 2026 sa.recycle(); 2027 return null; 2028 } 2029 2030 boolean success = true; 2031 2032 TypedValue v = sa.peekValue( 2033 com.android.internal.R.styleable.AndroidManifestMetaData_resource); 2034 if (v != null && v.resourceId != 0) { 2035 //Log.i(TAG, "Meta data ref " + name + ": " + v); 2036 data.putInt(name, v.resourceId); 2037 } else { 2038 v = sa.peekValue( 2039 com.android.internal.R.styleable.AndroidManifestMetaData_value); 2040 //Log.i(TAG, "Meta data " + name + ": " + v); 2041 if (v != null) { 2042 if (v.type == TypedValue.TYPE_STRING) { 2043 CharSequence cs = v.coerceToString(); 2044 data.putString(name, cs != null ? cs.toString() : null); 2045 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) { 2046 data.putBoolean(name, v.data != 0); 2047 } else if (v.type >= TypedValue.TYPE_FIRST_INT 2048 && v.type <= TypedValue.TYPE_LAST_INT) { 2049 data.putInt(name, v.data); 2050 } else if (v.type == TypedValue.TYPE_FLOAT) { 2051 data.putFloat(name, v.getFloat()); 2052 } else { 2053 if (!RIGID_PARSER) { 2054 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); 2055 Log.w(TAG, "<meta-data> only supports string, integer, float, color, boolean, and resource reference types"); 2056 } else { 2057 outError[0] = "<meta-data> only supports string, integer, float, color, boolean, and resource reference types"; 2058 data = null; 2059 } 2060 } 2061 } else { 2062 outError[0] = "<meta-data> requires an android:value or android:resource attribute"; 2063 data = null; 2064 } 2065 } 2066 2067 sa.recycle(); 2068 2069 XmlUtils.skipCurrentTag(parser); 2070 2071 return data; 2072 } 2073 2074 private static final String ANDROID_RESOURCES 2075 = "http://schemas.android.com/apk/res/android"; 2076 2077 private boolean parseIntent(Resources res, 2078 XmlPullParser parser, AttributeSet attrs, int flags, 2079 IntentInfo outInfo, String[] outError, boolean isActivity) 2080 throws XmlPullParserException, IOException { 2081 2082 TypedArray sa = res.obtainAttributes(attrs, 2083 com.android.internal.R.styleable.AndroidManifestIntentFilter); 2084 2085 int priority = sa.getInt( 2086 com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0); 2087 if (priority > 0 && isActivity && (flags&PARSE_IS_SYSTEM) == 0) { 2088 Log.w(TAG, "Activity with priority > 0, forcing to 0 at " 2089 + parser.getPositionDescription()); 2090 priority = 0; 2091 } 2092 outInfo.setPriority(priority); 2093 2094 TypedValue v = sa.peekValue( 2095 com.android.internal.R.styleable.AndroidManifestIntentFilter_label); 2096 if (v != null && (outInfo.labelRes=v.resourceId) == 0) { 2097 outInfo.nonLocalizedLabel = v.coerceToString(); 2098 } 2099 2100 outInfo.icon = sa.getResourceId( 2101 com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0); 2102 2103 sa.recycle(); 2104 2105 int outerDepth = parser.getDepth(); 2106 int type; 2107 while ((type=parser.next()) != parser.END_DOCUMENT 2108 && (type != parser.END_TAG || parser.getDepth() > outerDepth)) { 2109 if (type == parser.END_TAG || type == parser.TEXT) { 2110 continue; 2111 } 2112 2113 String nodeName = parser.getName(); 2114 if (nodeName.equals("action")) { 2115 String value = attrs.getAttributeValue( 2116 ANDROID_RESOURCES, "name"); 2117 if (value == null || value == "") { 2118 outError[0] = "No value supplied for <android:name>"; 2119 return false; 2120 } 2121 XmlUtils.skipCurrentTag(parser); 2122 2123 outInfo.addAction(value); 2124 } else if (nodeName.equals("category")) { 2125 String value = attrs.getAttributeValue( 2126 ANDROID_RESOURCES, "name"); 2127 if (value == null || value == "") { 2128 outError[0] = "No value supplied for <android:name>"; 2129 return false; 2130 } 2131 XmlUtils.skipCurrentTag(parser); 2132 2133 outInfo.addCategory(value); 2134 2135 } else if (nodeName.equals("data")) { 2136 sa = res.obtainAttributes(attrs, 2137 com.android.internal.R.styleable.AndroidManifestData); 2138 2139 String str = sa.getNonResourceString( 2140 com.android.internal.R.styleable.AndroidManifestData_mimeType); 2141 if (str != null) { 2142 try { 2143 outInfo.addDataType(str); 2144 } catch (IntentFilter.MalformedMimeTypeException e) { 2145 outError[0] = e.toString(); 2146 sa.recycle(); 2147 return false; 2148 } 2149 } 2150 2151 str = sa.getNonResourceString( 2152 com.android.internal.R.styleable.AndroidManifestData_scheme); 2153 if (str != null) { 2154 outInfo.addDataScheme(str); 2155 } 2156 2157 String host = sa.getNonResourceString( 2158 com.android.internal.R.styleable.AndroidManifestData_host); 2159 String port = sa.getNonResourceString( 2160 com.android.internal.R.styleable.AndroidManifestData_port); 2161 if (host != null) { 2162 outInfo.addDataAuthority(host, port); 2163 } 2164 2165 str = sa.getNonResourceString( 2166 com.android.internal.R.styleable.AndroidManifestData_path); 2167 if (str != null) { 2168 outInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL); 2169 } 2170 2171 str = sa.getNonResourceString( 2172 com.android.internal.R.styleable.AndroidManifestData_pathPrefix); 2173 if (str != null) { 2174 outInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX); 2175 } 2176 2177 str = sa.getNonResourceString( 2178 com.android.internal.R.styleable.AndroidManifestData_pathPattern); 2179 if (str != null) { 2180 outInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB); 2181 } 2182 2183 sa.recycle(); 2184 XmlUtils.skipCurrentTag(parser); 2185 } else if (!RIGID_PARSER) { 2186 Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); 2187 Log.w(TAG, "Unknown element under <intent-filter>: " + parser.getName()); 2188 XmlUtils.skipCurrentTag(parser); 2189 } else { 2190 outError[0] = "Bad element under <intent-filter>: " + parser.getName(); 2191 return false; 2192 } 2193 } 2194 2195 outInfo.hasDefault = outInfo.hasCategory(Intent.CATEGORY_DEFAULT); 2196 if (false) { 2197 String cats = ""; 2198 Iterator<String> it = outInfo.categoriesIterator(); 2199 while (it != null && it.hasNext()) { 2200 cats += " " + it.next(); 2201 } 2202 System.out.println("Intent d=" + 2203 outInfo.hasDefault + ", cat=" + cats); 2204 } 2205 2206 return true; 2207 } 2208 2209 public final static class Package { 2210 public final String packageName; 2211 2212 // For now we only support one application per package. 2213 public final ApplicationInfo applicationInfo = new ApplicationInfo(); 2214 2215 public final ArrayList<Permission> permissions = new ArrayList<Permission>(0); 2216 public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0); 2217 public final ArrayList<Activity> activities = new ArrayList<Activity>(0); 2218 public final ArrayList<Activity> receivers = new ArrayList<Activity>(0); 2219 public final ArrayList<Provider> providers = new ArrayList<Provider>(0); 2220 public final ArrayList<Service> services = new ArrayList<Service>(0); 2221 public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0); 2222 2223 public final ArrayList<String> requestedPermissions = new ArrayList<String>(); 2224 2225 public final ArrayList<String> usesLibraries = new ArrayList<String>(); 2226 public String[] usesLibraryFiles = null; 2227 2228 // We store the application meta-data independently to avoid multiple unwanted references 2229 public Bundle mAppMetaData = null; 2230 2231 public final ArrayList<Integer> supportsDensityList = new ArrayList<Integer>(); 2232 public int[] supportsDensities = null; 2233 2234 // If this is a 3rd party app, this is the path of the zip file. 2235 public String mPath; 2236 2237 // The version code declared for this package. 2238 public int mVersionCode; 2239 2240 // The version name declared for this package. 2241 public String mVersionName; 2242 2243 // The shared user id that this package wants to use. 2244 public String mSharedUserId; 2245 2246 // The shared user label that this package wants to use. 2247 public int mSharedUserLabel; 2248 2249 // Signatures that were read from the package. 2250 public Signature mSignatures[]; 2251 2252 // For use by package manager service for quick lookup of 2253 // preferred up order. 2254 public int mPreferredOrder = 0; 2255 2256 // Additional data supplied by callers. 2257 public Object mExtras; 2258 2259 /* 2260 * Applications hardware preferences 2261 */ 2262 public final ArrayList<ConfigurationInfo> configPreferences = 2263 new ArrayList<ConfigurationInfo>(); 2264 2265 public Package(String _name) { 2266 packageName = _name; 2267 applicationInfo.packageName = _name; 2268 applicationInfo.uid = -1; 2269 } 2270 2271 public String toString() { 2272 return "Package{" 2273 + Integer.toHexString(System.identityHashCode(this)) 2274 + " " + packageName + "}"; 2275 } 2276 } 2277 2278 public static class Component<II extends IntentInfo> { 2279 public final Package owner; 2280 public final ArrayList<II> intents; 2281 public final ComponentName component; 2282 public final String componentShortName; 2283 public Bundle metaData; 2284 2285 public Component(Package _owner) { 2286 owner = _owner; 2287 intents = null; 2288 component = null; 2289 componentShortName = null; 2290 } 2291 2292 public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) { 2293 owner = args.owner; 2294 intents = new ArrayList<II>(0); 2295 String name = args.sa.getNonResourceString(args.nameRes); 2296 if (name == null) { 2297 component = null; 2298 componentShortName = null; 2299 args.outError[0] = args.tag + " does not specify android:name"; 2300 return; 2301 } 2302 2303 outInfo.name 2304 = buildClassName(owner.applicationInfo.packageName, name, args.outError); 2305 if (outInfo.name == null) { 2306 component = null; 2307 componentShortName = null; 2308 args.outError[0] = args.tag + " does not have valid android:name"; 2309 return; 2310 } 2311 2312 component = new ComponentName(owner.applicationInfo.packageName, 2313 outInfo.name); 2314 componentShortName = component.flattenToShortString(); 2315 2316 int iconVal = args.sa.getResourceId(args.iconRes, 0); 2317 if (iconVal != 0) { 2318 outInfo.icon = iconVal; 2319 outInfo.nonLocalizedLabel = null; 2320 } 2321 2322 TypedValue v = args.sa.peekValue(args.labelRes); 2323 if (v != null && (outInfo.labelRes=v.resourceId) == 0) { 2324 outInfo.nonLocalizedLabel = v.coerceToString(); 2325 } 2326 2327 outInfo.packageName = owner.packageName; 2328 } 2329 2330 public Component(final ParseComponentArgs args, final ComponentInfo outInfo) { 2331 this(args, (PackageItemInfo)outInfo); 2332 if (args.outError[0] != null) { 2333 return; 2334 } 2335 2336 if (args.processRes != 0) { 2337 outInfo.processName = buildProcessName(owner.applicationInfo.packageName, 2338 owner.applicationInfo.processName, args.sa.getNonResourceString(args.processRes), 2339 args.flags, args.sepProcesses, args.outError); 2340 } 2341 outInfo.enabled = args.sa.getBoolean(args.enabledRes, true); 2342 } 2343 2344 public Component(Component<II> clone) { 2345 owner = clone.owner; 2346 intents = clone.intents; 2347 component = clone.component; 2348 componentShortName = clone.componentShortName; 2349 metaData = clone.metaData; 2350 } 2351 } 2352 2353 public final static class Permission extends Component<IntentInfo> { 2354 public final PermissionInfo info; 2355 public boolean tree; 2356 public PermissionGroup group; 2357 2358 public Permission(Package _owner) { 2359 super(_owner); 2360 info = new PermissionInfo(); 2361 } 2362 2363 public Permission(Package _owner, PermissionInfo _info) { 2364 super(_owner); 2365 info = _info; 2366 } 2367 2368 public String toString() { 2369 return "Permission{" 2370 + Integer.toHexString(System.identityHashCode(this)) 2371 + " " + info.name + "}"; 2372 } 2373 } 2374 2375 public final static class PermissionGroup extends Component<IntentInfo> { 2376 public final PermissionGroupInfo info; 2377 2378 public PermissionGroup(Package _owner) { 2379 super(_owner); 2380 info = new PermissionGroupInfo(); 2381 } 2382 2383 public PermissionGroup(Package _owner, PermissionGroupInfo _info) { 2384 super(_owner); 2385 info = _info; 2386 } 2387 2388 public String toString() { 2389 return "PermissionGroup{" 2390 + Integer.toHexString(System.identityHashCode(this)) 2391 + " " + info.name + "}"; 2392 } 2393 } 2394 2395 private static boolean copyNeeded(int flags, Package p, Bundle metaData) { 2396 if ((flags & PackageManager.GET_META_DATA) != 0 2397 && (metaData != null || p.mAppMetaData != null)) { 2398 return true; 2399 } 2400 if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0 2401 && p.usesLibraryFiles != null) { 2402 return true; 2403 } 2404 if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0 2405 && p.supportsDensities != null) { 2406 return true; 2407 } 2408 return false; 2409 } 2410 2411 public static ApplicationInfo generateApplicationInfo(Package p, int flags) { 2412 if (p == null) return null; 2413 if (!copyNeeded(flags, p, null)) { 2414 return p.applicationInfo; 2415 } 2416 2417 // Make shallow copy so we can store the metadata/libraries safely 2418 ApplicationInfo ai = new ApplicationInfo(p.applicationInfo); 2419 if ((flags & PackageManager.GET_META_DATA) != 0) { 2420 ai.metaData = p.mAppMetaData; 2421 } 2422 if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) { 2423 ai.sharedLibraryFiles = p.usesLibraryFiles; 2424 } 2425 if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0) { 2426 ai.supportsDensities = p.supportsDensities; 2427 } 2428 return ai; 2429 } 2430 2431 public static final PermissionInfo generatePermissionInfo( 2432 Permission p, int flags) { 2433 if (p == null) return null; 2434 if ((flags&PackageManager.GET_META_DATA) == 0) { 2435 return p.info; 2436 } 2437 PermissionInfo pi = new PermissionInfo(p.info); 2438 pi.metaData = p.metaData; 2439 return pi; 2440 } 2441 2442 public static final PermissionGroupInfo generatePermissionGroupInfo( 2443 PermissionGroup pg, int flags) { 2444 if (pg == null) return null; 2445 if ((flags&PackageManager.GET_META_DATA) == 0) { 2446 return pg.info; 2447 } 2448 PermissionGroupInfo pgi = new PermissionGroupInfo(pg.info); 2449 pgi.metaData = pg.metaData; 2450 return pgi; 2451 } 2452 2453 public final static class Activity extends Component<ActivityIntentInfo> { 2454 public final ActivityInfo info; 2455 2456 public Activity(final ParseComponentArgs args, final ActivityInfo _info) { 2457 super(args, _info); 2458 info = _info; 2459 info.applicationInfo = args.owner.applicationInfo; 2460 } 2461 2462 public String toString() { 2463 return "Activity{" 2464 + Integer.toHexString(System.identityHashCode(this)) 2465 + " " + component.flattenToString() + "}"; 2466 } 2467 } 2468 2469 public static final ActivityInfo generateActivityInfo(Activity a, 2470 int flags) { 2471 if (a == null) return null; 2472 if (!copyNeeded(flags, a.owner, a.metaData)) { 2473 return a.info; 2474 } 2475 // Make shallow copies so we can store the metadata safely 2476 ActivityInfo ai = new ActivityInfo(a.info); 2477 ai.metaData = a.metaData; 2478 ai.applicationInfo = generateApplicationInfo(a.owner, flags); 2479 return ai; 2480 } 2481 2482 public final static class Service extends Component<ServiceIntentInfo> { 2483 public final ServiceInfo info; 2484 2485 public Service(final ParseComponentArgs args, final ServiceInfo _info) { 2486 super(args, _info); 2487 info = _info; 2488 info.applicationInfo = args.owner.applicationInfo; 2489 } 2490 2491 public String toString() { 2492 return "Service{" 2493 + Integer.toHexString(System.identityHashCode(this)) 2494 + " " + component.flattenToString() + "}"; 2495 } 2496 } 2497 2498 public static final ServiceInfo generateServiceInfo(Service s, int flags) { 2499 if (s == null) return null; 2500 if (!copyNeeded(flags, s.owner, s.metaData)) { 2501 return s.info; 2502 } 2503 // Make shallow copies so we can store the metadata safely 2504 ServiceInfo si = new ServiceInfo(s.info); 2505 si.metaData = s.metaData; 2506 si.applicationInfo = generateApplicationInfo(s.owner, flags); 2507 return si; 2508 } 2509 2510 public final static class Provider extends Component { 2511 public final ProviderInfo info; 2512 public boolean syncable; 2513 2514 public Provider(final ParseComponentArgs args, final ProviderInfo _info) { 2515 super(args, _info); 2516 info = _info; 2517 info.applicationInfo = args.owner.applicationInfo; 2518 syncable = false; 2519 } 2520 2521 public Provider(Provider existingProvider) { 2522 super(existingProvider); 2523 this.info = existingProvider.info; 2524 this.syncable = existingProvider.syncable; 2525 } 2526 2527 public String toString() { 2528 return "Provider{" 2529 + Integer.toHexString(System.identityHashCode(this)) 2530 + " " + info.name + "}"; 2531 } 2532 } 2533 2534 public static final ProviderInfo generateProviderInfo(Provider p, 2535 int flags) { 2536 if (p == null) return null; 2537 if (!copyNeeded(flags, p.owner, p.metaData) 2538 && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0 2539 || p.info.uriPermissionPatterns == null)) { 2540 return p.info; 2541 } 2542 // Make shallow copies so we can store the metadata safely 2543 ProviderInfo pi = new ProviderInfo(p.info); 2544 pi.metaData = p.metaData; 2545 if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) { 2546 pi.uriPermissionPatterns = null; 2547 } 2548 pi.applicationInfo = generateApplicationInfo(p.owner, flags); 2549 return pi; 2550 } 2551 2552 public final static class Instrumentation extends Component { 2553 public final InstrumentationInfo info; 2554 2555 public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) { 2556 super(args, _info); 2557 info = _info; 2558 } 2559 2560 public String toString() { 2561 return "Instrumentation{" 2562 + Integer.toHexString(System.identityHashCode(this)) 2563 + " " + component.flattenToString() + "}"; 2564 } 2565 } 2566 2567 public static final InstrumentationInfo generateInstrumentationInfo( 2568 Instrumentation i, int flags) { 2569 if (i == null) return null; 2570 if ((flags&PackageManager.GET_META_DATA) == 0) { 2571 return i.info; 2572 } 2573 InstrumentationInfo ii = new InstrumentationInfo(i.info); 2574 ii.metaData = i.metaData; 2575 return ii; 2576 } 2577 2578 public static class IntentInfo extends IntentFilter { 2579 public boolean hasDefault; 2580 public int labelRes; 2581 public CharSequence nonLocalizedLabel; 2582 public int icon; 2583 } 2584 2585 public final static class ActivityIntentInfo extends IntentInfo { 2586 public final Activity activity; 2587 2588 public ActivityIntentInfo(Activity _activity) { 2589 activity = _activity; 2590 } 2591 2592 public String toString() { 2593 return "ActivityIntentInfo{" 2594 + Integer.toHexString(System.identityHashCode(this)) 2595 + " " + activity.info.name + "}"; 2596 } 2597 } 2598 2599 public final static class ServiceIntentInfo extends IntentInfo { 2600 public final Service service; 2601 2602 public ServiceIntentInfo(Service _service) { 2603 service = _service; 2604 } 2605 2606 public String toString() { 2607 return "ServiceIntentInfo{" 2608 + Integer.toHexString(System.identityHashCode(this)) 2609 + " " + service.info.name + "}"; 2610 } 2611 } 2612} 2613