Merge change 1642 into donut
* changes:
Allow the build system to build Objective-C sources.
diff --git a/cleanspec.mk b/cleanspec.mk
index 50a1f1f..14c8016 100644
--- a/cleanspec.mk
+++ b/cleanspec.mk
@@ -78,6 +78,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj)
$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/tcpdump)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/location)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/core/Makefile b/core/Makefile
index fa0550c..110b3c9 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -131,6 +131,7 @@
TARGET_BOOTLOADER_BOARD_NAME="$(TARGET_BOOTLOADER_BOARD_NAME)" \
BUILD_FINGERPRINT="$(BUILD_FINGERPRINT)" \
TARGET_BOARD_PLATFORM="$(TARGET_BOARD_PLATFORM)" \
+ TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \
bash $(BUILDINFO_SH) > $@
$(hide) if [ -f $(TARGET_DEVICE_DIR)/system.prop ]; then \
cat $(TARGET_DEVICE_DIR)/system.prop >> $@; \
diff --git a/core/apicheck_msg_current.txt b/core/apicheck_msg_current.txt
index c277ecd..d723a19 100644
--- a/core/apicheck_msg_current.txt
+++ b/core/apicheck_msg_current.txt
@@ -6,12 +6,11 @@
1) You can add "@hide" javadoc comments to the methods, etc. listed in the
errors above.
- 2) You can update current.xml by executing the following commands:
+ 2) You can update current.xml by executing the following command:
- p4 edit frameworks/base/api/current.xml
make update-api
- To check in the revised current.xml, you will need OWNERS approval.
+ To check in the revised current.xml, you will need approval from the android API council.
******************************
diff --git a/core/main.mk b/core/main.mk
index 655a592..2bf8102 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -406,6 +406,10 @@
# Clean up/verify variables defined by the board config file.
TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME))
+TARGET_CPU_ABI := $(strip $(TARGET_CPU_ABI))
+ifeq ($(TARGET_CPU_ABI),)
+ $(error No TARGET_CPU_ABI defined by board config: $(board_config_mk))
+endif
#
# Include all of the makefiles in the system
diff --git a/core/pathmap.mk b/core/pathmap.mk
index 13cb80d..de7c1bb 100644
--- a/core/pathmap.mk
+++ b/core/pathmap.mk
@@ -82,6 +82,7 @@
opengl \
sax \
telephony \
+ tts \
wifi \
)
diff --git a/target/board/generic/BoardConfig.mk b/target/board/generic/BoardConfig.mk
index a874742..6ec2de3 100644
--- a/target/board/generic/BoardConfig.mk
+++ b/target/board/generic/BoardConfig.mk
@@ -7,5 +7,6 @@
TARGET_NO_BOOTLOADER := true
TARGET_NO_KERNEL := true
TARGET_NO_RADIOIMAGE := true
+TARGET_CPU_ABI := armeabi
HAVE_HTC_AUDIO_DRIVER := true
BOARD_USES_GENERIC_AUDIO := true
diff --git a/target/board/sim/BoardConfig.mk b/target/board/sim/BoardConfig.mk
index 92679d9..491b30f 100644
--- a/target/board/sim/BoardConfig.mk
+++ b/target/board/sim/BoardConfig.mk
@@ -17,6 +17,9 @@
# Don't bother with a kernel
TARGET_NO_KERNEL := true
+# The simulator does not support native code at all
+TARGET_CPU_ABI := none
+
#the simulator partially emulates the original HTC /dev/eac audio interface
HAVE_HTC_AUDIO_DRIVER := true
BOARD_USES_GENERIC_AUDIO := true
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
index 1fcac76..5c738a2 100755
--- a/tools/buildinfo.sh
+++ b/tools/buildinfo.sh
@@ -20,6 +20,7 @@
echo "ro.product.name=$PRODUCT_NAME"
echo "ro.product.device=$TARGET_DEVICE"
echo "ro.product.board=$TARGET_BOOTLOADER_BOARD_NAME"
+echo "ro.product.cpu.abi=$TARGET_CPU_ABI"
echo "ro.product.manufacturer=$PRODUCT_MANUFACTURER"
echo "ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE"
echo "ro.product.locale.region=$PRODUCT_DEFAULT_REGION"
diff --git a/tools/droiddoc/templates-pdk/customization.cs b/tools/droiddoc/templates-pdk/customization.cs
index 01b6e96..563af1e 100644
--- a/tools/droiddoc/templates-pdk/customization.cs
+++ b/tools/droiddoc/templates-pdk/customization.cs
@@ -5,7 +5,7 @@
def:custom_masthead() ?>
<div id="header">
<div id="headerLeft">
- <a href="<?cs var:toroot ?>index.html" tabindex="-1"><img
+ <a href="<?cs var:toroot ?>guide/index.html" tabindex="-1"><img
src="<?cs var:toroot ?>assets/images/open_source.png" alt="Open Source Project: Platform Development Kit" /></a>
<ul class="<?cs
if:reference ?> <?cs
@@ -15,7 +15,7 @@
elif:community ?> <?cs
elif:publish ?> <?cs
elif:about ?> <?cs /if ?>">
- <li id="guide-link"><a href="<?cs var:toroot ?>index.html"
+ <li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html"
onClick="return loadLast('guide)'"><span>Porting Guide</span></a></li>
<li id="opensource-link"><a href="http://source.android.com/"
onClick="return loadLast('open')"><span>Open Source</span></a></li>
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index a512ff8..51a6d8f 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import errno
import getopt
import getpass
import os
@@ -132,13 +133,14 @@
those which require them. Return a {key: password} dict. password
will be None if the key has no password."""
- key_passwords = {}
+ no_passwords = []
+ need_passwords = []
devnull = open("/dev/null", "w+b")
for k in sorted(keylist):
# An empty-string key is used to mean don't re-sign this package.
# Obviously we don't need a password for this non-key.
if not k:
- key_passwords[k] = None
+ no_passwords.append(k)
continue
p = subprocess.Popen(["openssl", "pkcs8", "-in", k+".pk8",
@@ -148,12 +150,13 @@
stderr=subprocess.STDOUT)
p.communicate()
if p.returncode == 0:
- print "%s.pk8 does not require a password" % (k,)
- key_passwords[k] = None
+ no_passwords.append(k)
else:
- key_passwords[k] = getpass.getpass("Enter password for %s.pk8> " % (k,))
+ need_passwords.append(k)
devnull.close()
- print
+
+ key_passwords = PasswordManager().GetPasswords(need_passwords)
+ key_passwords.update(dict.fromkeys(no_passwords, None))
return key_passwords
@@ -278,3 +281,102 @@
shutil.rmtree(i)
else:
os.remove(i)
+
+
+class PasswordManager(object):
+ def __init__(self):
+ self.editor = os.getenv("EDITOR", None)
+ self.pwfile = os.getenv("ANDROID_PW_FILE", None)
+
+ def GetPasswords(self, items):
+ """Get passwords corresponding to each string in 'items',
+ returning a dict. (The dict may have keys in addition to the
+ values in 'items'.)
+
+ Uses the passwords in $ANDROID_PW_FILE if available, letting the
+ user edit that file to add more needed passwords. If no editor is
+ available, or $ANDROID_PW_FILE isn't define, prompts the user
+ interactively in the ordinary way.
+ """
+
+ current = self.ReadFile()
+
+ first = True
+ while True:
+ missing = []
+ for i in items:
+ if i not in current or not current[i]:
+ missing.append(i)
+ # Are all the passwords already in the file?
+ if not missing: return current
+
+ for i in missing:
+ current[i] = ""
+
+ if not first:
+ print "key file %s still missing some passwords." % (self.pwfile,)
+ answer = raw_input("try to edit again? [y]> ").strip()
+ if answer and answer[0] not in 'yY':
+ raise RuntimeError("key passwords unavailable")
+ first = False
+
+ current = self.UpdateAndReadFile(current)
+
+ def PromptResult(self, current):
+ """Prompt the user to enter a value (password) for each key in
+ 'current' whose value is fales. Returns a new dict with all the
+ values.
+ """
+ result = {}
+ for k, v in sorted(current.iteritems()):
+ if v:
+ result[k] = v
+ else:
+ while True:
+ result[k] = getpass.getpass("Enter password for %s key> "
+ % (k,)).strip()
+ if result[k]: break
+ return result
+
+ def UpdateAndReadFile(self, current):
+ if not self.editor or not self.pwfile:
+ return self.PromptResult(current)
+
+ f = open(self.pwfile, "w")
+ os.chmod(self.pwfile, 0600)
+ f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
+ f.write("# (Additional spaces are harmless.)\n\n")
+
+ first_line = None
+ sorted = [(not v, k, v) for (k, v) in current.iteritems()]
+ sorted.sort()
+ for i, (_, k, v) in enumerate(sorted):
+ f.write("[[[ %s ]]] %s\n" % (v, k))
+ if not v and first_line is None:
+ # position cursor on first line with no password.
+ first_line = i + 4
+ f.close()
+
+ p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
+ _, _ = p.communicate()
+
+ return self.ReadFile()
+
+ def ReadFile(self):
+ result = {}
+ if self.pwfile is None: return result
+ try:
+ f = open(self.pwfile, "r")
+ for line in f:
+ line = line.strip()
+ if not line or line[0] == '#': continue
+ m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
+ if not m:
+ print "failed to parse password file: ", line
+ else:
+ result[m.group(2)] = m.group(1)
+ f.close()
+ except IOError, e:
+ if e.errno != errno.ENOENT:
+ print "error reading password file: ", str(e)
+ return result
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index 69e9f5b..364f751 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -42,6 +42,9 @@
the build scripts (used for developer OTA packages which
legitimately need to go back and forth).
+ -e (--extra_script) <file>
+ Insert the contents of file at the end of the update script.
+
"""
import sys
@@ -69,6 +72,7 @@
OPTIONS.patch_threshold = 0.95
OPTIONS.wipe_user_data = False
OPTIONS.omit_prereq = False
+OPTIONS.extra_script = None
def MostPopularKey(d, default):
"""Given a dict, return the key corresponding to the largest
@@ -363,6 +367,9 @@
script.append("write_raw_image PACKAGE:boot.img BOOT:")
script.append("show_progress 0.2 10")
+ if OPTIONS.extra_script is not None:
+ script.append(OPTIONS.extra_script)
+
AddScript(script, output_zip)
@@ -612,6 +619,9 @@
script.append("show_progress 0.1 5")
script.append("write_raw_image PACKAGE:boot.img BOOT:")
+ if OPTIONS.extra_script is not None:
+ script.append(OPTIONS.extra_script)
+
AddScript(script, output_zip)
@@ -628,17 +638,20 @@
OPTIONS.wipe_user_data = True
elif o in ("-n", "--no_prereq"):
OPTIONS.omit_prereq = True
+ elif o in ("-e", "--extra_script"):
+ OPTIONS.extra_script = a
else:
return False
return True
args = common.ParseOptions(argv, __doc__,
- extra_opts="b:k:i:d:wn",
+ extra_opts="b:k:i:d:wne:",
extra_long_opts=["board_config=",
"package_key=",
"incremental_from=",
"wipe_user_data",
- "no_prereq"],
+ "no_prereq",
+ "extra_script="],
extra_option_handler=option_handler)
if len(args) != 2:
@@ -652,6 +665,9 @@
print " images don't exceed partition sizes."
print
+ if OPTIONS.extra_script is not None:
+ OPTIONS.extra_script = open(OPTIONS.extra_script).read()
+
print "unzipping target target-files..."
OPTIONS.input_tmp = common.UnzipTemp(args[0])
OPTIONS.target_tmp = OPTIONS.input_tmp
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index b3bfaee..9f393c8 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -101,6 +101,85 @@
return certmap
+def CheckAllApksSigned(input_tf_zip, apk_key_map):
+ """Check that all the APKs we want to sign have keys specified, and
+ error out if they don't."""
+ unknown_apks = []
+ for info in input_tf_zip.infolist():
+ if info.filename.endswith(".apk"):
+ name = os.path.basename(info.filename)
+ if name not in apk_key_map:
+ unknown_apks.append(name)
+ if unknown_apks:
+ print "ERROR: no key specified for:\n\n ",
+ print "\n ".join(unknown_apks)
+ print "\nUse '-e <apkname>=' to specify a key (which may be an"
+ print "empty string to not sign this apk)."
+ sys.exit(1)
+
+
+def SharedUserForApk(data):
+ tmp = tempfile.NamedTemporaryFile()
+ tmp.write(data)
+ tmp.flush()
+
+ p = common.Run(["aapt", "dump", "xmltree", tmp.name, "AndroidManifest.xml"],
+ stdout=subprocess.PIPE)
+ data, _ = p.communicate()
+ if p.returncode != 0:
+ raise ExternalError("failed to run aapt dump")
+ lines = data.split("\n")
+ for i in lines:
+ m = re.match(r'^\s*A: android:sharedUserId\([0-9a-fx]*\)="([^"]*)" .*$', i)
+ if m:
+ return m.group(1)
+ return None
+
+
+def CheckSharedUserIdsConsistent(input_tf_zip, apk_key_map):
+ """Check that all packages that request the same shared user id are
+ going to be signed with the same key."""
+
+ shared_user_apks = {}
+ maxlen = len("(unknown key)")
+
+ for info in input_tf_zip.infolist():
+ if info.filename.endswith(".apk"):
+ data = input_tf_zip.read(info.filename)
+
+ name = os.path.basename(info.filename)
+ shared_user = SharedUserForApk(data)
+ key = apk_key_map[name]
+ maxlen = max(maxlen, len(key))
+
+ if shared_user is not None:
+ shared_user_apks.setdefault(
+ shared_user, {}).setdefault(key, []).append(name)
+
+ errors = []
+ for k, v in shared_user_apks.iteritems():
+ # each shared user should have exactly one key used for all the
+ # apks that want that user.
+ if len(v) > 1:
+ errors.append((k, v))
+
+ if not errors: return
+
+ print "ERROR: shared user inconsistency. All apks wanting to use"
+ print " a given shared user must be signed with the same key."
+ print
+ errors.sort()
+ for user, keys in errors:
+ print 'shared user id "%s":' % (user,)
+ for key, apps in keys.iteritems():
+ print ' %-*s %s' % (maxlen, key or "(unknown key)", apps[0])
+ for a in apps[1:]:
+ print (' ' * (maxlen+5)) + a
+ print
+
+ sys.exit(1)
+
+
def SignApk(data, keyname, pw):
unsigned = tempfile.NamedTemporaryFile()
unsigned.write(data)
@@ -117,31 +196,11 @@
return data
-def SignApks(input_tf_zip, output_tf_zip):
- apk_key_map = GetApkCerts(input_tf_zip)
-
+def SignApks(input_tf_zip, output_tf_zip, apk_key_map, key_passwords):
maxsize = max([len(os.path.basename(i.filename))
for i in input_tf_zip.infolist()
if i.filename.endswith('.apk')])
- # Check that all the APKs we want to sign have keys specified, and
- # error out if they don't. Do this before prompting for key
- # passwords in case we're going to fail anyway.
- unknown_apks = []
- for info in input_tf_zip.infolist():
- if info.filename.endswith(".apk"):
- name = os.path.basename(info.filename)
- if name not in apk_key_map:
- unknown_apks.append(name)
- if unknown_apks:
- print "ERROR: no key specified for:\n\n ",
- print "\n ".join(unknown_apks)
- print "\nUse '-e <apkname>=' to specify a key (which may be an"
- print "empty string to not sign this apk)."
- sys.exit(1)
-
- key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
-
for info in input_tf_zip.infolist():
data = input_tf_zip.read(info.filename)
out_info = copy.copy(info)
@@ -289,7 +348,12 @@
input_zip = zipfile.ZipFile(args[0], "r")
output_zip = zipfile.ZipFile(args[1], "w")
- SignApks(input_zip, output_zip)
+ apk_key_map = GetApkCerts(input_zip)
+ CheckAllApksSigned(input_zip, apk_key_map)
+ CheckSharedUserIdsConsistent(input_zip, apk_key_map)
+
+ key_passwords = common.GetKeyPasswords(set(apk_key_map.values()))
+ SignApks(input_zip, output_zip, apk_key_map, key_passwords)
if OPTIONS.replace_ota_keys:
ReplaceOtaKeys(input_zip, output_zip)