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