diff --git a/code/chapter-02/default.xml b/code/chapter-02/default.xml new file mode 100644 index 0000000..10ce3a6 --- /dev/null +++ b/code/chapter-02/default.xml @@ -0,0 +1,1095 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/chapter-02/initGit.py b/code/chapter-02/initGit.py new file mode 100644 index 0000000..1e9d3fc --- /dev/null +++ b/code/chapter-02/initGit.py @@ -0,0 +1,119 @@ +#!/usr/bin/python3 + +import gitlab +import os +import re +import time + +MANIFEST_XML = "default.xml" +ROOT = os.getcwd() +ROOT_GROUP = "Android6NewC" +MANIFEST_XML_PATH_NAME_RE = re.compile(r"[^\"]+)\"\s+name=\"(?P[^\"]+)\"\s+/>", + re.DOTALL) + +gl = gitlab.Gitlab('http://192.168.50.10/', private_token='xxxxxxx') + +manifest_xml_project_paths = [] + +def parse_repo_manifest(): + with open(os.path.join(ROOT, MANIFEST_XML), "rb") as strReader: + for line in strReader: + if line is not None and len(line) != 0: + this_temp_line = line.decode() + if line.find("path".encode(encoding="utf-8")): + + s = MANIFEST_XML_PATH_NAME_RE.search(this_temp_line) + + if s is not None: + manifest_xml_project_paths.append(s.group("path")) + + print("manifest_xml_project_paths=" + str(manifest_xml_project_paths)) + print("manifest_xml_project_paths len=" + str(len(manifest_xml_project_paths))) + +def create_group_and_project(): + all_groups = gl.groups.list(all=True) + print("all_groups=" + str(all_groups)) + group_parent = None + + for g in all_groups: + if g.name == ROOT_GROUP: + group_parent = g + break + print("group parent=" + str(group_parent)) + + for path in manifest_xml_project_paths: + print("path=" + path) + paths = path.split("/") + print("paths=" + str(paths)) + + last_path_index = len(paths) - 1 + + group = group_parent + for index in range(0, last_path_index): + p = paths[index] + print("p=" + p) + # is the group exist + print("parent group=" + group.name) + try: + all_groups = group.subgroups.list(all=True) + except AttributeError: + all_groups = [] + print("AttributeError: clear all subgroups") + + is_group_exist = False + for g in all_groups: + if g.name == p: + is_group_exist = True + group = g + print("group exist=" + g.name) + break + if is_group_exist: + continue + # create subgroup + data = { + "name": p, + "path": p, + "parent_id": group.id + } + + try: + group = gl.groups.create(data) + print("group create success name=" + p) + time.sleep(1) + except gitlab.exceptions.GitlabCreateError as e: + if e.response_code == 400: + print("group:" + p + " has already been created") + + query_groups = gl.groups.list(all=True) + print("query_groups:" + str(query_groups)) + for query_group in query_groups: + if query_group.name == p and query_group.parent_id == group.id: + group = query_group + print("update exit group:" + group.name) + break + + project = paths[last_path_index] + print("group project list group=" + group.name) + real_group = gl.groups.get(group.id, lazy=True) + all_projects = real_group.projects.list(all=True) + print("group all projects=" + str(all_projects)) + is_project_exist = False + for p in all_projects: + if p.name == project: + is_project_exist = True + print("project exist=" + p.name) + break + if not is_project_exist: + print("create project=" + project) + gl.projects.create({'name': project, 'path': project, 'namespace_id': group.id}) + print("project create success name=" + project) + time.sleep(1) + +def test_create_project_with_dot_name(): + # need use path field, if don't use path, GitLab url will replace "." to "_" + gl.projects.create({'name': "xxx.yy.xy", 'path': "xxx.yy.xy"}) + +if __name__ == '__main__': + parse_repo_manifest() + create_group_and_project() + diff --git a/code/chapter-02/putGit.py b/code/chapter-02/putGit.py new file mode 100644 index 0000000..e286cf5 --- /dev/null +++ b/code/chapter-02/putGit.py @@ -0,0 +1,115 @@ +#!/usr/bin/python3 + +import os +import re,time,subprocess + +MANIFEST_XML = "./default.xml" +ROOT = os.getcwd() +LOG_FILE_PATH = os.path.join(ROOT, "push.log") + +MANIFEST_XML_PATH_NAME_RE = re.compile(r"[^\"]+)\"\s+name=\"(?P[^\"]+)\"\s+", + re.DOTALL) +SOURCE_CODE_ROOT = "/home/king/android_src/aosp12_mikrom/" +REMOTE = "git@192.168.2.189:mikrom12/" +manifest_xml_project_paths = [] + +def parse_repo_manifest(): + with open(os.path.join(ROOT, MANIFEST_XML), "rb") as strReader: + for line in strReader: + if line is not None and len(line) != 0: + this_temp_line = line.decode() + if line.find("path".encode(encoding="utf-8")): + + s = MANIFEST_XML_PATH_NAME_RE.search(this_temp_line) + + if s is not None: + manifest_xml_project_paths.append({"path":s.group("path"),"name":s.group("name")}) + + print("manifest_xml_project_paths=" + str(manifest_xml_project_paths)) + print("manifest_xml_project_paths len=" + str(len(manifest_xml_project_paths))) + +def exec(cmd): + proc = subprocess.Popen( + cmd, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE # ÖØ¶¨ÏòÊäÈëÖµ + ) + proc.stdin.close() # ¼ÈȻûÓÐÃüÁîÐд°¿Ú£¬ÄǾ͹رÕÊäÈë + result = proc.stdout.read() # ¶ÁÈ¡cmdÖ´ÐеÄÊä³ö½á¹û£¨ÊÇbyteÀàÐÍ£¬ÐèÒªdecode£© + proc.stdout.close() + return result.decode(encoding="utf-8") + +def push_source_code_by_folder(str_writer): + for path in manifest_xml_project_paths: + print("path=" + path["path"]) + abs_path = SOURCE_CODE_ROOT + path["path"] + + if os.path.exists(abs_path): + # change current work dir + os.chdir(abs_path + "/") + res= exec("git remote -v") + print(res) + if path["name"] in res: + continue + # 1\. delete .git & .gitignore folder + rm_git_cmd = "rm -rf .git" + rm_gitignore_cmd = "rm -rf .gitignore" + os.system(rm_git_cmd) + os.system(rm_gitignore_cmd) + + # 2\. list dir + dir_data = os.listdir(os.getcwd()) + + cmd_list = [] + + print("changed cwd=" + os.getcwd()) + + if len(dir_data) == 0: + echo_cmd = "echo \"This is a empty repository.\" > ReadMe.md" + str_writer.write(f"empty repository:{abs_path}".encode() ) + str_writer.write("\r\n".encode()) + cmd_list.append(echo_cmd) + + git_init_cmd = "git init" + cmd_list.append(git_init_cmd) + + git_remote_cmd = "git remote add origin " + REMOTE + path["name"] + ".git" + cmd_list.append(git_remote_cmd) + + git_add_dot_cmd = "git add ." + cmd_list.append(git_add_dot_cmd) + + git_commit_cmd = "git commit -m \"Initial commit\"" + cmd_list.append(git_commit_cmd) + + git_push_cmd = "git push -u origin master" + cmd_list.append(git_push_cmd) + + for cmd in cmd_list: + print("begin exec cmd=" + cmd) + os.system(cmd) + print("end exec cmd=" + cmd) + else: + print("abs_path=" + abs_path + " is not exist.") + str_writer.write(f"folder not exist:{abs_path}".encode() ) + str_writer.write("\r\n".encode()) + +def wrapper_push_source_code_write_log(): + with open(LOG_FILE_PATH, 'wb+') as strWriter: + push_source_code_by_folder(strWriter) + strWriter.close() + +# def test_only_dot_git_folder(): +# subdir_and_file = os.listdir(os.getcwd()) +# print("subdir_and_file=" + str(subdir_and_file)) +# with open(LOG_FILE_PATH, 'wb+') as strWriter: +# strWriter.write(str(subdir_and_file).encode()) +# strWriter.write("\r\n".encode()) +# strWriter.write(str(subdir_and_file).encode()) +# strWriter.close() + +if __name__ == '__main__': + parse_repo_manifest() + wrapper_push_source_code_write_log() diff --git a/code/chapter-04/replaceIcon.py b/code/chapter-04/replaceIcon.py new file mode 100644 index 0000000..da6666c --- /dev/null +++ b/code/chapter-04/replaceIcon.py @@ -0,0 +1,71 @@ +import os +import shutil +import subprocess + +# Ö´ÐÐcmdÃüÁî +def exec(cmd): + proc = subprocess.Popen( + cmd, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE # ÖØ¶¨ÏòÊäÈëÖµ + ) + proc.stdin.close() # ¼ÈȻûÓÐÃüÁîÐд°¿Ú£¬ÄǾ͹رÕÊäÈë + result = proc.stdout.read() # ¶ÁÈ¡cmdÖ´ÐеÄÊä³ö½á¹û£¨ÊÇbyteÀàÐÍ£¬ÐèÒªdecode£© + proc.stdout.close() + return result.decode(encoding="utf-8") + +# Ìæ»»Í¼±ê +def replacePng(target,appName): + # ËÑË÷¸Ã·¾¶ÏµÄͼ±ê + cmdRes = exec(f"find /home/king/android_src/mikrom12_gitlab/packages/ -name {target}") + filePathList = cmdRes.split("\n") + curpath=os.getcwd() + # ±éÀúËùÓÐËѵ½µÄ½á¹û + for filepath in filePathList: + if filepath=="": + continue + # ΪÁ˱ÜÃâÆäËûÓ¦ÓõÄͬÃûËØ²Äͼ±ê£¬ËùÒÔʹÓÃappName¹ýÂËһϠ+ if appName not in filepath: + continue + print('Found file: ' + filepath) + # ÏȽ«Îļþ½øÐб¸·Ý + shutil.copy(filepath,filepath+".bak") + # È»ºó½«µ±Ç°Ä¿Â¼×¼±¸ºÃµÄÌæ»»Îļþ¸´ÖƽøÈ¥ + replacePath=curpath+"/images/"+target + # Èç¹ûÐÂÎļþ²»´æÔÚ£¬Ôò½áÊø¸ÃÎļþµÄÌæ»» + if os.path.exists(replacePath)==False: + print("not found replace file:",replacePath) + break + shutil.copy(replacePath, filepath) + +# ʹÓñ¸·ÝµÄÎļþ»¹Ô­¸Ãͼ±ê +def unReplacePng(target): + # ²éÕÒÄ¿±êÎļþ + cmdRes = exec(f"find /home/king/android_src/mikrom12_gitlab/frameworks/base/packages/ -name {target}") + filePathList = cmdRes.split("\n") + # ±éÀúËùÓнá¹û + for filepath in filePathList: + if filepath=="": + continue + print('Found file: ' + filepath) + # ±¸·ÝÎļþÈç¹û´æÔÚ£¬Ôò½«Æä»¹Ô­ + bakfile=filepath + ".bak" + if os.path.exists(bakfile): + shutil.copy(bakfile, filepath) + print("unReplace file:",bakfile) + +def main(): + # Ìæ»»ÎªÐÂËØ²Ä + replacePng('ic_launcher_settings.png',"Setting") + replacePng('ic_contacts_launcher.png',"Contacts") + replacePng('ic_launcher_calendar.png',"Calendar") + + # »¹Ô­ËØ²Ä + # unReplacePng('ic_launcher_settings.png') + # unReplacePng('ic_contacts_launcher.png') + # unReplacePng('ic_launcher_calendar.png') + +if __name__ == '__main__': + main() diff --git a/code/chapter-05/SystemAppDemo/Android.mk b/code/chapter-05/SystemAppDemo/Android.mk new file mode 100644 index 0000000..7b6926c --- /dev/null +++ b/code/chapter-05/SystemAppDemo/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := SystemAppDemo.apk +LOCAL_MODULE := SystemAppDemo +LOCAL_MODULE_CLASS := APPS +LOCAL_MODULE_TAGS := optional +LOCAL_CERTIFICATE := platform +LOCAL_DEX_PREOPT := false + +include $(BUILD_PREBUILT) \ No newline at end of file diff --git a/code/chapter-05/SystemAppDemo/SystemAppDemo.apk b/code/chapter-05/SystemAppDemo/SystemAppDemo.apk new file mode 100644 index 0000000..7f59049 Binary files /dev/null and b/code/chapter-05/SystemAppDemo/SystemAppDemo.apk differ diff --git a/code/chapter-05/mysodemo/Android.mk b/code/chapter-05/mysodemo/Android.mk new file mode 100644 index 0000000..2954cc4 --- /dev/null +++ b/code/chapter-05/mysodemo/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) + +#-------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := libmysodemo +LOCAL_SRC_FILES_arm := libmysodemo.so +LOCAL_SRC_FILES_arm64 := libmysodemo_arm64.so +LOCAL_MODULE_TARGET_ARCHS:= arm arm64 +LOCAL_MULTILIB := both +LOCAL_MODULE_SUFFIX := .so +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES := liblog + +include $(BUILD_PREBUILT) diff --git a/code/chapter-05/mysodemo/libmysodemo.so b/code/chapter-05/mysodemo/libmysodemo.so new file mode 100644 index 0000000..e077920 Binary files /dev/null and b/code/chapter-05/mysodemo/libmysodemo.so differ diff --git a/code/chapter-05/mysodemo/libmysodemo_arm64.so b/code/chapter-05/mysodemo/libmysodemo_arm64.so new file mode 100644 index 0000000..ae0e737 Binary files /dev/null and b/code/chapter-05/mysodemo/libmysodemo_arm64.so differ diff --git a/code/chapter-06/ParsingPackageUtils.java b/code/chapter-06/ParsingPackageUtils.java new file mode 100644 index 0000000..e7be5cc --- /dev/null +++ b/code/chapter-06/ParsingPackageUtils.java @@ -0,0 +1,3250 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm.parsing; + +import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; +import static android.os.Build.VERSION_CODES.DONUT; +import static android.os.Build.VERSION_CODES.O; +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + +import android.annotation.AnyRes; +import android.annotation.CheckResult; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StyleableRes; +import android.app.ActivityThread; +import android.app.ResourcesManager; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureGroupInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.Property; +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.SigningDetails; +import android.content.pm.Signature; +import android.content.pm.parsing.component.ComponentParseUtils; +import android.content.pm.parsing.component.ParsedActivity; +import android.content.pm.parsing.component.ParsedActivityUtils; +import android.content.pm.parsing.component.ParsedAttribution; +import android.content.pm.parsing.component.ParsedAttributionUtils; +import android.content.pm.parsing.component.ParsedComponent; +import android.content.pm.parsing.component.ParsedInstrumentation; +import android.content.pm.parsing.component.ParsedInstrumentationUtils; +import android.content.pm.parsing.component.ParsedIntentInfo; +import android.content.pm.parsing.component.ParsedIntentInfoUtils; +import android.content.pm.parsing.component.ParsedMainComponent; +import android.content.pm.parsing.component.ParsedPermission; +import android.content.pm.parsing.component.ParsedPermissionGroup; +import android.content.pm.parsing.component.ParsedPermissionUtils; +import android.content.pm.parsing.component.ParsedProcess; +import android.content.pm.parsing.component.ParsedProcessUtils; +import android.content.pm.parsing.component.ParsedProvider; +import android.content.pm.parsing.component.ParsedProviderUtils; +import android.content.pm.parsing.component.ParsedService; +import android.content.pm.parsing.component.ParsedServiceUtils; +import android.content.pm.parsing.component.ParsedUsesPermission; +import android.content.pm.parsing.result.ParseInput; +import android.content.pm.parsing.result.ParseInput.DeferredError; +import android.content.pm.parsing.result.ParseResult; +import android.content.pm.parsing.result.ParseTypeImpl; +import android.content.pm.split.DefaultSplitAssetLoader; +import android.content.pm.split.SplitAssetDependencyLoader; +import android.content.pm.split.SplitAssetLoader; +import android.content.res.ApkAssets; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.FileUtils; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.Trace; +import android.os.UserHandle; +import android.os.ext.SdkExtensions; +import android.permission.PermissionManager; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Pair; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.TypedValue; +import android.util.apk.ApkSignatureVerifier; + +import com.android.internal.R; +import com.android.internal.os.ClassLoaderFactory; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.XmlUtils; + +import libcore.io.IoUtils; +import libcore.util.EmptyArray; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.StringTokenizer; +import java.krom.Reflect; + +/** + * TODO(b/135203078): Differentiate between parse_ methods and some add_ method for whether it + * mutates the passed-in component or not. Or consolidate so all parse_ methods mutate. + * + * @hide + */ +public class ParsingPackageUtils { + + private static final String TAG = ParsingUtils.TAG; + + public static final boolean DEBUG_JAR = false; + public static final boolean DEBUG_BACKUP = false; + public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f; + + /** File name in an APK for the Android manifest. */ + public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"; + + /** Path prefix for apps on expanded storage */ + public static final String MNT_EXPAND = "/mnt/expand/"; + + public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions"; + public static final String TAG_APPLICATION = "application"; + public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens"; + public static final String TAG_EAT_COMMENT = "eat-comment"; + public static final String TAG_FEATURE_GROUP = "feature-group"; + public static final String TAG_INSTRUMENTATION = "instrumentation"; + public static final String TAG_KEY_SETS = "key-sets"; + public static final String TAG_MANIFEST = "manifest"; + public static final String TAG_ORIGINAL_PACKAGE = "original-package"; + public static final String TAG_OVERLAY = "overlay"; + public static final String TAG_PACKAGE = "package"; + public static final String TAG_PACKAGE_VERIFIER = "package-verifier"; + public static final String TAG_ATTRIBUTION = "attribution"; + public static final String TAG_PERMISSION = "permission"; + public static final String TAG_PERMISSION_GROUP = "permission-group"; + public static final String TAG_PERMISSION_TREE = "permission-tree"; + public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast"; + public static final String TAG_QUERIES = "queries"; + public static final String TAG_RESTRICT_UPDATE = "restrict-update"; + public static final String TAG_SUPPORT_SCREENS = "supports-screens"; + public static final String TAG_SUPPORTS_INPUT = "supports-input"; + public static final String TAG_USES_CONFIGURATION = "uses-configuration"; + public static final String TAG_USES_FEATURE = "uses-feature"; + public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; + public static final String TAG_USES_PERMISSION = "uses-permission"; + public static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23"; + public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m"; + public static final String TAG_USES_SDK = "uses-sdk"; + public static final String TAG_USES_SPLIT = "uses-split"; + public static final String TAG_PROFILEABLE = "profileable"; + + public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect"; + public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes"; + public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY = + "android.activity_window_layout_affinity"; + public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode"; + + public static final int SDK_VERSION = Build.VERSION.SDK_INT; + public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; + + public static boolean sCompatibilityModeEnabled = true; + public static boolean sUseRoundIcon = false; + + public static final int PARSE_DEFAULT_INSTALL_LOCATION = + PackageInfo.INSTALL_LOCATION_UNSPECIFIED; + public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1; + + /** If set to true, we will only allow package files that exactly match + * the DTD. Otherwise, we try to get as much from the package as we + * can without failing. This should normally be set to false, to + * support extensions to the DTD in future versions. */ + public static final boolean RIGID_PARSER = false; + + public static final int PARSE_MUST_BE_APK = 1 << 0; + public static final int PARSE_IGNORE_PROCESSES = 1 << 1; + public static final int PARSE_EXTERNAL_STORAGE = 1 << 3; + public static final int PARSE_IS_SYSTEM_DIR = 1 << 4; + public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5; + public static final int PARSE_ENFORCE_CODE = 1 << 6; + public static final int PARSE_CHATTY = 1 << 31; + + @IntDef(flag = true, prefix = { "PARSE_" }, value = { + PARSE_CHATTY, + PARSE_COLLECT_CERTIFICATES, + PARSE_ENFORCE_CODE, + PARSE_EXTERNAL_STORAGE, + PARSE_IGNORE_PROCESSES, + PARSE_IS_SYSTEM_DIR, + PARSE_MUST_BE_APK, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ParseFlags {} + + /** + * For those names would be used as a part of the file name. Limits size to 223 and reserves 32 + * for the OS. + */ + private static final int MAX_FILE_NAME_SIZE = 223; + + /** + * @see #parseDefault(ParseInput, File, int, List, boolean) + */ + @NonNull + public static ParseResult parseDefaultOneTime(File file, + @ParseFlags int parseFlags, + @NonNull List splitPermissions, + boolean collectCertificates) { + ParseInput input = ParseTypeImpl.forDefaultParsing().reset(); + return parseDefault(input, file, parseFlags, splitPermissions, collectCertificates); + } + + /** + * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off + * request, without caching the input object and without querying the internal system state + * for feature support. + */ + @NonNull + public static ParseResult parseDefault(ParseInput input, File file, + @ParseFlags int parseFlags, + @NonNull List splitPermissions, + boolean collectCertificates) { + ParseResult result; + + ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, splitPermissions, + new Callback() { + @Override + public boolean hasFeature(String feature) { + // Assume the device doesn't support anything. This will affect permission + // parsing and will force declarations to include all + // requiredNotFeature permissions and exclude all requiredFeature + // permissions. This mirrors the old behavior. + return false; + } + + @Override + public ParsingPackage startParsingPackage( + @NonNull String packageName, + @NonNull String baseApkPath, + @NonNull String path, + @NonNull TypedArray manifestArray, boolean isCoreApp) { + return new ParsingPackageImpl(packageName, baseApkPath, path, + manifestArray); + } + }); + try { + result = parser.parsePackage(input, file, parseFlags); + if (result.isError()) { + return result; + } + } catch (PackageParser.PackageParserException e) { + return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Error parsing package", e); + } + + try { + ParsingPackage pkg = result.getResult(); + if (collectCertificates) { + pkg.setSigningDetails( + ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */)); + } + + // Need to call this to finish the parsing stage + pkg.hideAsParsed(); + + return input.success(pkg); + } catch (PackageParser.PackageParserException e) { + return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Error collecting package certificates", e); + } + } + + private boolean mOnlyCoreApps; + private String[] mSeparateProcesses; + private DisplayMetrics mDisplayMetrics; + @NonNull + private List mSplitPermissionInfos; + private Callback mCallback; + + public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses, + DisplayMetrics displayMetrics, + @NonNull List splitPermissions, + @NonNull Callback callback) { + mOnlyCoreApps = onlyCoreApps; + mSeparateProcesses = separateProcesses; + mDisplayMetrics = displayMetrics; + mSplitPermissionInfos = splitPermissions; + mCallback = callback; + } + + /** + * Parse the package at the given location. Automatically detects if the + * package is a monolithic style (single APK file) or cluster style + * (directory of APKs). + *

+ * This performs validity checking on cluster style packages, such as + * requiring identical package name and version codes, a single base APK, + * and unique split names. + *

+ * Note that this does not perform signature verification; that + * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}. + * + * If {@code useCaches} is true, the package parser might return a cached + * result from a previous parse of the same {@code packageFile} with the same + * {@code flags}. Note that this method does not check whether {@code packageFile} + * has changed since the last parse, it's up to callers to do so. + * + * @see PackageParser#parsePackageLite(File, int) + */ + public ParseResult parsePackage(ParseInput input, File packageFile, + int flags) + throws PackageParserException { + if (packageFile.isDirectory()) { + return parseClusterPackage(input, packageFile, flags); + } else { + return parseMonolithicPackage(input, packageFile, flags); + } + } + + /** + * Parse all APKs contained in the given directory, treating them as a + * single package. This also performs validity checking, such as requiring + * identical package name and version codes, a single base APK, and unique + * split names. + *

+ * Note that this does not perform signature verification; that + * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}. + */ + private ParseResult parseClusterPackage(ParseInput input, File packageDir, + int flags) { + final ParseResult liteResult = + ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0); + if (liteResult.isError()) { + return input.error(liteResult); + } + + final PackageLite lite = liteResult.getResult(); + if (mOnlyCoreApps && !lite.isCoreApp()) { + return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED, + "Not a coreApp: " + packageDir); + } + + // Build the split dependency tree. + SparseArray splitDependencies = null; + final SplitAssetLoader assetLoader; + if (lite.isIsolatedSplits() && !ArrayUtils.isEmpty(lite.getSplitNames())) { + try { + splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite); + assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags); + } catch (SplitAssetDependencyLoader.IllegalDependencyException e) { + return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage()); + } + } else { + assetLoader = new DefaultSplitAssetLoader(lite, flags); + } + + try { + final File baseApk = new File(lite.getBaseApkPath()); + final ParseResult result = parseBaseApk(input, baseApk, + lite.getPath(), assetLoader, flags); + if (result.isError()) { + return input.error(result); + } + + ParsingPackage pkg = result.getResult(); + if (!ArrayUtils.isEmpty(lite.getSplitNames())) { + pkg.asSplit( + lite.getSplitNames(), + lite.getSplitApkPaths(), + lite.getSplitRevisionCodes(), + splitDependencies + ); + final int num = lite.getSplitNames().length; + + for (int i = 0; i < num; i++) { + final AssetManager splitAssets = assetLoader.getSplitAssetManager(i); + parseSplitApk(input, pkg, i, splitAssets, flags); + } + } + + pkg.setUse32BitAbi(lite.isUse32bitAbi()); + return input.success(pkg); + } catch (PackageParserException e) { + return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to load assets: " + lite.getBaseApkPath(), e); + } finally { + IoUtils.closeQuietly(assetLoader); + } + } + + /** + * Parse the given APK file, treating it as as a single monolithic package. + *

+ * Note that this does not perform signature verification; that + * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}. + */ + private ParseResult parseMonolithicPackage(ParseInput input, File apkFile, + int flags) throws PackageParserException { + final ParseResult liteResult = + ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags); + if (liteResult.isError()) { + return input.error(liteResult); + } + + final PackageLite lite = liteResult.getResult(); + if (mOnlyCoreApps && !lite.isCoreApp()) { + return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED, + "Not a coreApp: " + apkFile); + } + + final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); + try { + final ParseResult result = parseBaseApk(input, + apkFile, + apkFile.getCanonicalPath(), + assetLoader, flags); + if (result.isError()) { + return input.error(result); + } + + return input.success(result.getResult() + .setUse32BitAbi(lite.isUse32bitAbi())); + } catch (IOException e) { + return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to get path: " + apkFile, e); + } finally { + IoUtils.closeQuietly(assetLoader); + } + } + + private ParseResult parseBaseApk(ParseInput input, File apkFile, + String codePath, SplitAssetLoader assetLoader, int flags) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + + String volumeUuid = null; + if (apkPath.startsWith(MNT_EXPAND)) { + final int end = apkPath.indexOf('/', MNT_EXPAND.length()); + volumeUuid = apkPath.substring(MNT_EXPAND.length(), end); + } + + if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath); + + final AssetManager assets = assetLoader.getBaseAssetManager(); + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } + + try (XmlResourceParser parser = assets.openXmlResourceParser(cookie, + ANDROID_MANIFEST_FILENAME)) { + final Resources res = new Resources(assets, mDisplayMetrics, null); + + ParseResult result = parseBaseApk(input, apkPath, codePath, res, + parser, flags); + if (result.isError()) { + return input.error(result.getErrorCode(), + apkPath + " (at " + parser.getPositionDescription() + "): " + + result.getErrorMessage()); + } + + final ParsingPackage pkg = result.getResult(); + if (assets.containsAllocatedTable()) { + final ParseResult deferResult = input.deferError( + "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires" + + " the resources.arsc of installed APKs to be stored uncompressed" + + " and aligned on a 4-byte boundary", + DeferredError.RESOURCES_ARSC_COMPRESSED); + if (deferResult.isError()) { + return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED, + deferResult.getErrorMessage()); + } + } + + ApkAssets apkAssets = assetLoader.getBaseApkAssets(); + boolean definesOverlayable = false; + try { + definesOverlayable = apkAssets.definesOverlayable(); + } catch (IOException ignored) { + // Will fail if there's no packages in the ApkAssets, which can be treated as false + } + + if (definesOverlayable) { + SparseArray packageNames = assets.getAssignedPackageIdentifiers(); + int size = packageNames.size(); + for (int index = 0; index < size; index++) { + String packageName = packageNames.valueAt(index); + Map overlayableToActor = assets.getOverlayableMap(packageName); + if (overlayableToActor != null && !overlayableToActor.isEmpty()) { + for (String overlayable : overlayableToActor.keySet()) { + pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable)); + } + } + } + } + + pkg.setVolumeUuid(volumeUuid); + + if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { + pkg.setSigningDetails(getSigningDetails(pkg, false)); + } else { + pkg.setSigningDetails(SigningDetails.UNKNOWN); + } + + return input.success(pkg); + } catch (Exception e) { + return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to read manifest from " + apkPath, e); + } + } + + private ParseResult parseSplitApk(ParseInput input, + ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) { + final String apkPath = pkg.getSplitCodePaths()[splitIndex]; + + if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath); + + // This must always succeed, as the path has been added to the AssetManager before. + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } + try (XmlResourceParser parser = assets.openXmlResourceParser(cookie, + ANDROID_MANIFEST_FILENAME)) { + Resources res = new Resources(assets, mDisplayMetrics, null); + ParseResult parseResult = parseSplitApk(input, pkg, res, + parser, flags, splitIndex); + if (parseResult.isError()) { + return input.error(parseResult.getErrorCode(), + apkPath + " (at " + parser.getPositionDescription() + "): " + + parseResult.getErrorMessage()); + } + + return parseResult; + } catch (Exception e) { + return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to read manifest from " + apkPath, e); + } + } + + /** + * Parse the manifest of a base APK. When adding new features you + * need to consider whether they should be supported by split APKs and child + * packages. + * + * @param apkPath The package apk file path + * @param res The resources from which to resolve values + * @param parser The manifest parser + * @param flags Flags how to parse + * @return Parsed package or null on error. + */ + private ParseResult parseBaseApk(ParseInput input, String apkPath, + String codePath, Resources res, XmlResourceParser parser, int flags) + throws XmlPullParserException, IOException { + final String splitName; + final String pkgName; + + ParseResult> packageSplitResult = + ApkLiteParseUtils.parsePackageSplitNames(input, parser); + if (packageSplitResult.isError()) { + return input.error(packageSplitResult); + } + + Pair packageSplit = packageSplitResult.getResult(); + pkgName = packageSplit.first; + splitName = packageSplit.second; + + if (!TextUtils.isEmpty(splitName)) { + return input.error( + PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, + "Expected base APK, but found split " + splitName + ); + } + + final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest); + try { + final boolean isCoreApp = + parser.getAttributeBooleanValue(null, "coreApp", false); + final ParsingPackage pkg = mCallback.startParsingPackage( + pkgName, apkPath, codePath, manifestArray, isCoreApp); + final ParseResult result = + parseBaseApkTags(input, pkg, manifestArray, res, parser, flags); + if (result.isError()) { + return result; + } + + return input.success(pkg); + } finally { + manifestArray.recycle(); + } + } + + /** + * Parse the manifest of a split APK. + *

+ * Note that split APKs have many more restrictions on what they're capable + * of doing, so many valid features of a base APK have been carefully + * omitted here. + * + * @param pkg builder to fill + * @return false on failure + */ + private ParseResult parseSplitApk(ParseInput input, ParsingPackage pkg, + Resources res, XmlResourceParser parser, int flags, int splitIndex) + throws XmlPullParserException, IOException, PackageParserException { + AttributeSet attrs = parser; + + // We parsed manifest tag earlier; just skip past it + PackageParser.parsePackageSplitNames(parser, attrs); + + int type; + + boolean foundApp = false; + + int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) { + continue; + } + + final ParseResult result; + String tagName = parser.getName(); + if (TAG_APPLICATION.equals(tagName)) { + if (foundApp) { + if (RIGID_PARSER) { + result = input.error(" has more than one "); + } else { + Slog.w(TAG, " has more than one "); + result = input.success(null); + } + } else { + foundApp = true; + result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex); + } + } else { + result = ParsingUtils.unknownTag("", pkg, parser, input); + } + + if (result.isError()) { + return input.error(result); + } + } + + if (!foundApp) { + ParseResult deferResult = input.deferError( + " does not contain an ", DeferredError.MISSING_APP_TAG); + if (deferResult.isError()) { + return input.error(deferResult); + } + } + + return input.success(pkg); + } + + /** + * Parse the {@code application} XML tree at the current parse location in a + * split APK manifest. + *

+ * Note that split APKs have many more restrictions on what they're capable + * of doing, so many valid features of a base APK have been carefully + * omitted here. + */ + private ParseResult parseSplitApplication(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex) + throws XmlPullParserException, IOException { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication); + try { + pkg.setSplitHasCode(splitIndex, sa.getBoolean( + R.styleable.AndroidManifestApplication_hasCode, true)); + + final String classLoaderName = sa.getString( + R.styleable.AndroidManifestApplication_classLoader); + if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName( + classLoaderName)) { + pkg.setSplitClassLoaderName(splitIndex, classLoaderName); + } else { + return input.error("Invalid class loader name: " + classLoaderName); + } + } finally { + sa.recycle(); + } + final int depth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > depth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + ParsedMainComponent mainComponent = null; + + final ParseResult result; + String tagName = parser.getName(); + boolean isActivity = false; + switch (tagName) { + case "activity": + isActivity = true; + // fall-through + case "receiver": + ParseResult activityResult = + ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg, + res, + parser, flags, sUseRoundIcon, input); + if (activityResult.isSuccess()) { + ParsedActivity activity = activityResult.getResult(); + if (isActivity) { + pkg.addActivity(activity); + } else { + pkg.addReceiver(activity); + } + mainComponent = activity; + } + result = activityResult; + break; + case "service": + ParseResult serviceResult = ParsedServiceUtils.parseService( + mSeparateProcesses, pkg, res, parser, flags, + sUseRoundIcon, input); + if (serviceResult.isSuccess()) { + ParsedService service = serviceResult.getResult(); + pkg.addService(service); + mainComponent = service; + } + result = serviceResult; + break; + case "provider": + ParseResult providerResult = + ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser, + flags, sUseRoundIcon, input); + if (providerResult.isSuccess()) { + ParsedProvider provider = providerResult.getResult(); + pkg.addProvider(provider); + mainComponent = provider; + } + result = providerResult; + break; + case "activity-alias": + activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser, + sUseRoundIcon, input); + if (activityResult.isSuccess()) { + ParsedActivity activity = activityResult.getResult(); + pkg.addActivity(activity); + mainComponent = activity; + } + + result = activityResult; + break; + default: + result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser); + break; + } + + if (result.isError()) { + return input.error(result); + } + + if (mainComponent != null && mainComponent.getSplitName() == null) { + // If the loaded component did not specify a split, inherit the split name + // based on the split it is defined in. + // This is used to later load the correct split when starting this + // component. + mainComponent.setSplitName(pkg.getSplitNames()[splitIndex]); + } + } + + return input.success(pkg); + } + + /** + * For parsing non-MainComponents. Main ones have an order and some special handling which is + * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources, + * XmlResourceParser, int, int)}. + */ + private ParseResult parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg, + Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { + switch (tag) { + case "meta-data": + // note: application meta-data is stored off to the side, so it can + // remain null in the primary copy (we like to avoid extra copies because + // it can be large) + ParseResult metaDataResult = parseMetaData(pkg, null, res, + parser, "", input); + if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) { + pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData())); + } + return metaDataResult; + case "property": + ParseResult propertyResult = parseMetaData(pkg, null, res, + parser, "", input); + if (propertyResult.isSuccess()) { + pkg.addProperty(propertyResult.getResult()); + } + return propertyResult; + case "uses-static-library": + return parseUsesStaticLibrary(input, pkg, res, parser); + case "uses-library": + return parseUsesLibrary(input, pkg, res, parser); + case "uses-native-library": + return parseUsesNativeLibrary(input, pkg, res, parser); + case "uses-package": + // Dependencies for app installers; we don't currently try to + // enforce this. + return input.success(null); + default: + return ParsingUtils.unknownTag("", pkg, parser, input); + } + } + + private ParseResult parseBaseApkTags(ParseInput input, ParsingPackage pkg, + TypedArray sa, Resources res, XmlResourceParser parser, int flags) + throws XmlPullParserException, IOException { + ParseResult sharedUserResult = parseSharedUser(input, pkg, sa); + if (sharedUserResult.isError()) { + return sharedUserResult; + } + + pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION, + R.styleable.AndroidManifest_installLocation, sa)) + .setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX, + R.styleable.AndroidManifest_targetSandboxVersion, sa)) + /* Set the global "on SD card" flag */ + .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0); + + boolean foundApp = false; + final int depth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > depth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + String tagName = parser.getName(); + final ParseResult result; + + // has special logic, so it's handled outside the general method + if (TAG_APPLICATION.equals(tagName)) { + if (foundApp) { + if (RIGID_PARSER) { + result = input.error(" has more than one "); + } else { + Slog.w(TAG, " has more than one "); + result = input.success(null); + } + } else { + foundApp = true; + result = parseBaseApplication(input, pkg, res, parser, flags); + } + } else { + result = parseBaseApkTag(tagName, input, pkg, res, parser, flags); + } + + if (result.isError()) { + return input.error(result); + } + } + + if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) { + ParseResult deferResult = input.deferError( + " does not contain an or ", + DeferredError.MISSING_APP_TAG); + if (deferResult.isError()) { + return input.error(deferResult); + } + } + + if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) { + return input.error( + INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Combination tags are not valid" + ); + } + + convertNewPermissions(pkg); + + convertSplitPermissions(pkg); + + // At this point we can check if an application is not supporting densities and hence + // cannot be windowed / resized. Note that an SDK version of 0 is common for + // pre-Doughnut applications. + if (pkg.getTargetSdkVersion() < DONUT + || (!pkg.isSupportsSmallScreens() + && !pkg.isSupportsNormalScreens() + && !pkg.isSupportsLargeScreens() + && !pkg.isSupportsExtraLargeScreens() + && !pkg.isResizeable() + && !pkg.isAnyDensity())) { + adjustPackageToBeUnresizeableAndUnpipable(pkg); + } + + return input.success(pkg); + } + + private ParseResult parseBaseApkTag(String tag, ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags) + throws IOException, XmlPullParserException { + switch (tag) { + case TAG_OVERLAY: + return parseOverlay(input, pkg, res, parser); + case TAG_KEY_SETS: + return parseKeySets(input, pkg, res, parser); + case "feature": // TODO moltmann: Remove + case TAG_ATTRIBUTION: + return parseAttribution(input, pkg, res, parser); + case TAG_PERMISSION_GROUP: + return parsePermissionGroup(input, pkg, res, parser); + case TAG_PERMISSION: + return parsePermission(input, pkg, res, parser); + case TAG_PERMISSION_TREE: + return parsePermissionTree(input, pkg, res, parser); + case TAG_USES_PERMISSION: + case TAG_USES_PERMISSION_SDK_M: + case TAG_USES_PERMISSION_SDK_23: + return parseUsesPermission(input, pkg, res, parser); + case TAG_USES_CONFIGURATION: + return parseUsesConfiguration(input, pkg, res, parser); + case TAG_USES_FEATURE: + return parseUsesFeature(input, pkg, res, parser); + case TAG_FEATURE_GROUP: + return parseFeatureGroup(input, pkg, res, parser); + case TAG_USES_SDK: + return parseUsesSdk(input, pkg, res, parser); + case TAG_SUPPORT_SCREENS: + return parseSupportScreens(input, pkg, res, parser); + case TAG_PROTECTED_BROADCAST: + return parseProtectedBroadcast(input, pkg, res, parser); + case TAG_INSTRUMENTATION: + return parseInstrumentation(input, pkg, res, parser); + case TAG_ORIGINAL_PACKAGE: + return parseOriginalPackage(input, pkg, res, parser); + case TAG_ADOPT_PERMISSIONS: + return parseAdoptPermissions(input, pkg, res, parser); + case TAG_USES_GL_TEXTURE: + case TAG_COMPATIBLE_SCREENS: + case TAG_SUPPORTS_INPUT: + case TAG_EAT_COMMENT: + // Just skip this tag + XmlUtils.skipCurrentTag(parser); + return input.success(pkg); + case TAG_RESTRICT_UPDATE: + return parseRestrictUpdateHash(flags, input, pkg, res, parser); + case TAG_QUERIES: + return parseQueries(input, pkg, res, parser); + default: + return ParsingUtils.unknownTag("", pkg, parser, input); + } + } + + private static ParseResult parseSharedUser(ParseInput input, + ParsingPackage pkg, TypedArray sa) { + String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa); + if (TextUtils.isEmpty(str)) { + return input.success(pkg); + } + + if (!"android".equals(pkg.getPackageName())) { + ParseResult nameResult = validateName(input, str, true, true); + if (nameResult.isError()) { + return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, + " specifies bad sharedUserId name \"" + str + "\": " + + nameResult.getErrorMessage()); + } + } + + return input.success(pkg + .setSharedUserId(str.intern()) + .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa))); + } + + private static ParseResult parseKeySets(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + // we've encountered the 'key-sets' tag + // all the keys and keysets that we want must be defined here + // so we're going to iterate over the parser and pull out the things we want + int outerDepth = parser.getDepth(); + int currentKeySetDepth = -1; + int type; + String currentKeySet = null; + ArrayMap publicKeys = new ArrayMap<>(); + ArraySet upgradeKeySets = new ArraySet<>(); + ArrayMap> definedKeySets = new ArrayMap<>(); + ArraySet improperKeySets = new ArraySet<>(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG) { + if (parser.getDepth() == currentKeySetDepth) { + currentKeySet = null; + currentKeySetDepth = -1; + } + continue; + } + String tagName = parser.getName(); + switch (tagName) { + case "key-set": { + if (currentKeySet != null) { + return input.error("Improperly nested 'key-set' tag at " + + parser.getPositionDescription()); + } + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestKeySet); + try { + final String keysetName = sa.getNonResourceString( + R.styleable.AndroidManifestKeySet_name); + definedKeySets.put(keysetName, new ArraySet<>()); + currentKeySet = keysetName; + currentKeySetDepth = parser.getDepth(); + } finally { + sa.recycle(); + } + } break; + case "public-key": { + if (currentKeySet == null) { + return input.error("Improperly nested 'key-set' tag at " + + parser.getPositionDescription()); + } + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestPublicKey); + try { + final String publicKeyName = nonResString( + R.styleable.AndroidManifestPublicKey_name, sa); + final String encodedKey = nonResString( + R.styleable.AndroidManifestPublicKey_value, sa); + if (encodedKey == null && publicKeys.get(publicKeyName) == null) { + return input.error("'public-key' " + publicKeyName + + " must define a public-key value on first use at " + + parser.getPositionDescription()); + } else if (encodedKey != null) { + PublicKey currentKey = PackageParser.parsePublicKey(encodedKey); + if (currentKey == null) { + Slog.w(TAG, "No recognized valid key in 'public-key' tag at " + + parser.getPositionDescription() + " key-set " + + currentKeySet + + " will not be added to the package's defined key-sets."); + improperKeySets.add(currentKeySet); + XmlUtils.skipCurrentTag(parser); + continue; + } + if (publicKeys.get(publicKeyName) == null + || publicKeys.get(publicKeyName).equals(currentKey)) { + + /* public-key first definition, or matches old definition */ + publicKeys.put(publicKeyName, currentKey); + } else { + return input.error("Value of 'public-key' " + publicKeyName + + " conflicts with previously defined value at " + + parser.getPositionDescription()); + } + } + definedKeySets.get(currentKeySet).add(publicKeyName); + XmlUtils.skipCurrentTag(parser); + } finally { + sa.recycle(); + } + } break; + case "upgrade-key-set": { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestUpgradeKeySet); + try { + String name = sa.getNonResourceString( + R.styleable.AndroidManifestUpgradeKeySet_name); + upgradeKeySets.add(name); + XmlUtils.skipCurrentTag(parser); + } finally { + sa.recycle(); + } + } break; + default: + ParseResult result = ParsingUtils.unknownTag("", pkg, parser, + input); + if (result.isError()) { + return input.error(result); + } + break; + } + } + String packageName = pkg.getPackageName(); + Set publicKeyNames = publicKeys.keySet(); + if (publicKeyNames.removeAll(definedKeySets.keySet())) { + return input.error("Package" + packageName + + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct."); + } + + for (ArrayMap.Entry> e : definedKeySets.entrySet()) { + final String keySetName = e.getKey(); + if (e.getValue().size() == 0) { + Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml " + + "'key-set' " + keySetName + " has no valid associated 'public-key'." + + " Not including in package's defined key-sets."); + continue; + } else if (improperKeySets.contains(keySetName)) { + Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml " + + "'key-set' " + keySetName + " contained improper 'public-key'" + + " tags. Not including in package's defined key-sets."); + continue; + } + + for (String s : e.getValue()) { + pkg.addKeySet(keySetName, publicKeys.get(s)); + } + } + if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) { + pkg.setUpgradeKeySets(upgradeKeySets); + } else { + return input.error("Package" + packageName + + " AndroidManifest.xml does not define all 'upgrade-key-set's ."); + } + + return input.success(pkg); + } + + private static ParseResult parseAttribution(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws IOException, XmlPullParserException { + ParseResult result = ParsedAttributionUtils.parseAttribution(res, + parser, input); + if (result.isError()) { + return input.error(result); + } + return input.success(pkg.addAttribution(result.getResult())); + } + + private static ParseResult parsePermissionGroup(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + ParseResult result = ParsedPermissionUtils.parsePermissionGroup( + pkg, res, parser, sUseRoundIcon, input); + if (result.isError()) { + return input.error(result); + } + return input.success(pkg.addPermissionGroup(result.getResult())); + } + + private static ParseResult parsePermission(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + ParseResult result = ParsedPermissionUtils.parsePermission( + pkg, res, parser, sUseRoundIcon, input); + if (result.isError()) { + return input.error(result); + } + return input.success(pkg.addPermission(result.getResult())); + } + + private static ParseResult parsePermissionTree(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + ParseResult result = ParsedPermissionUtils.parsePermissionTree( + pkg, res, parser, sUseRoundIcon, input); + if (result.isError()) { + return input.error(result); + } + return input.success(pkg.addPermission(result.getResult())); + } + + private ParseResult parseUsesPermission(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws IOException, XmlPullParserException { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesPermission); + try { + + // Note: don't allow this value to be a reference to a resource + // that may change. + String name = sa.getNonResourceString( + R.styleable.AndroidManifestUsesPermission_name); + + int maxSdkVersion = 0; + TypedValue val = sa.peekValue( + R.styleable.AndroidManifestUsesPermission_maxSdkVersion); + if (val != null) { + if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) { + maxSdkVersion = val.data; + } + } + + final ArraySet requiredFeatures = new ArraySet<>(); + String feature = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, + 0); + if (feature != null) { + requiredFeatures.add(feature); + } + + final ArraySet requiredNotFeatures = new ArraySet<>(); + feature = sa.getNonConfigurationString( + com.android.internal.R.styleable + .AndroidManifestUsesPermission_requiredNotFeature, + 0); + if (feature != null) { + requiredNotFeatures.add(feature); + } + + final int usesPermissionFlags = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_usesPermissionFlags, + 0); + + final int outerDepth = parser.getDepth(); + int type; + + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + final ParseResult result; + switch (parser.getName()) { + case "required-feature": + result = parseRequiredFeature(input, res, parser); + if (result.isSuccess()) { + requiredFeatures.add((String) result.getResult()); + } + break; + + case "required-not-feature": + result = parseRequiredNotFeature(input, res, parser); + if (result.isSuccess()) { + requiredNotFeatures.add((String) result.getResult()); + } + break; + + default: + result = ParsingUtils.unknownTag("", pkg, parser, input); + break; + } + + if (result.isError()) { + return input.error(result); + } + } + + // Can only succeed from here on out + ParseResult success = input.success(pkg); + + if (name == null) { + return success; + } + + if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) { + return success; + } + + if (mCallback != null) { + // Only allow requesting this permission if the platform supports all of the + // "required-feature"s. + for (int i = requiredFeatures.size() - 1; i >= 0; i--) { + if (!mCallback.hasFeature(requiredFeatures.valueAt(i))) { + return success; + } + } + + // Only allow requesting this permission if the platform does not supports any of + // the "required-not-feature"s. + for (int i = requiredNotFeatures.size() - 1; i >= 0; i--) { + if (mCallback.hasFeature(requiredNotFeatures.valueAt(i))) { + return success; + } + } + } + + // Quietly ignore duplicate permission requests, but fail loudly if + // the two requests have conflicting flags + boolean found = false; + final List usesPermissions = pkg.getUsesPermissions(); + final int size = usesPermissions.size(); + for (int i = 0; i < size; i++) { + final ParsedUsesPermission usesPermission = usesPermissions.get(i); + if (Objects.equals(usesPermission.name, name)) { + if (usesPermission.usesPermissionFlags != usesPermissionFlags) { + return input.error("Conflicting uses-permissions flags: " + + name + " in package: " + pkg.getPackageName() + " at: " + + parser.getPositionDescription()); + } else { + Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: " + + name + " in package: " + pkg.getPackageName() + " at: " + + parser.getPositionDescription()); + } + found = true; + break; + } + } + + if (!found) { + pkg.addUsesPermission(new ParsedUsesPermission(name, usesPermissionFlags)); + } + return success; + } finally { + sa.recycle(); + } + } + + private ParseResult parseRequiredFeature(ParseInput input, Resources res, + AttributeSet attrs) { + final TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestRequiredFeature); + try { + final String featureName = sa.getString( + R.styleable.AndroidManifestRequiredFeature_name); + return TextUtils.isEmpty(featureName) + ? input.error("Feature name is missing from tag.") + : input.success(featureName); + } finally { + sa.recycle(); + } + } + + private ParseResult parseRequiredNotFeature(ParseInput input, Resources res, + AttributeSet attrs) { + final TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestRequiredNotFeature); + try { + final String featureName = sa.getString( + R.styleable.AndroidManifestRequiredNotFeature_name); + return TextUtils.isEmpty(featureName) + ? input.error("Feature name is missing from tag.") + : input.success(featureName); + } finally { + sa.recycle(); + } + } + + private static ParseResult parseUsesConfiguration(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + ConfigurationInfo cPref = new ConfigurationInfo(); + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesConfiguration); + try { + cPref.reqTouchScreen = sa.getInt( + R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen, + Configuration.TOUCHSCREEN_UNDEFINED); + cPref.reqKeyboardType = sa.getInt( + R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType, + Configuration.KEYBOARD_UNDEFINED); + if (sa.getBoolean( + R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard, + false)) { + cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD; + } + cPref.reqNavigation = sa.getInt( + R.styleable.AndroidManifestUsesConfiguration_reqNavigation, + Configuration.NAVIGATION_UNDEFINED); + if (sa.getBoolean( + R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav, + false)) { + cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV; + } + pkg.addConfigPreference(cPref); + return input.success(pkg); + } finally { + sa.recycle(); + } + } + + private static ParseResult parseUsesFeature(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + FeatureInfo fi = parseFeatureInfo(res, parser); + pkg.addReqFeature(fi); + + if (fi.name == null) { + ConfigurationInfo cPref = new ConfigurationInfo(); + cPref.reqGlEsVersion = fi.reqGlEsVersion; + pkg.addConfigPreference(cPref); + } + + return input.success(pkg); + } + + private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) { + FeatureInfo fi = new FeatureInfo(); + TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestUsesFeature); + try { + // Note: don't allow this value to be a reference to a resource + // that may change. + fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name); + fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0); + if (fi.name == null) { + fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion, + FeatureInfo.GL_ES_VERSION_UNDEFINED); + } + if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) { + fi.flags |= FeatureInfo.FLAG_REQUIRED; + } + return fi; + } finally { + sa.recycle(); + } + } + + private static ParseResult parseFeatureGroup(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws IOException, XmlPullParserException { + FeatureGroupInfo group = new FeatureGroupInfo(); + ArrayList features = null; + final int depth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > depth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + final String innerTagName = parser.getName(); + if (innerTagName.equals("uses-feature")) { + FeatureInfo featureInfo = parseFeatureInfo(res, parser); + // FeatureGroups are stricter and mandate that + // any declared are mandatory. + featureInfo.flags |= FeatureInfo.FLAG_REQUIRED; + features = ArrayUtils.add(features, featureInfo); + } else { + Slog.w(TAG, + "Unknown element under : " + innerTagName + + " at " + pkg.getBaseApkPath() + " " + + parser.getPositionDescription()); + } + } + + if (features != null) { + group.features = new FeatureInfo[features.size()]; + group.features = features.toArray(group.features); + } + + pkg.addFeatureGroup(group); + return input.success(pkg); + } + + private static ParseResult parseUsesSdk(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws IOException, XmlPullParserException { + if (SDK_VERSION > 0) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk); + try { + int minVers = ParsingUtils.DEFAULT_MIN_SDK_VERSION; + String minCode = null; + int targetVers = ParsingUtils.DEFAULT_TARGET_SDK_VERSION; + String targetCode = null; + + TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion); + if (val != null) { + if (val.type == TypedValue.TYPE_STRING && val.string != null) { + minCode = val.string.toString(); + } else { + // If it's not a string, it's an integer. + minVers = val.data; + } + } + + val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion); + if (val != null) { + if (val.type == TypedValue.TYPE_STRING && val.string != null) { + targetCode = val.string.toString(); + if (minCode == null) { + minCode = targetCode; + } + } else { + // If it's not a string, it's an integer. + targetVers = val.data; + } + } else { + targetVers = minVers; + targetCode = minCode; + } + + ParseResult targetSdkVersionResult = computeTargetSdkVersion( + targetVers, targetCode, SDK_CODENAMES, input); + if (targetSdkVersionResult.isError()) { + return input.error(targetSdkVersionResult); + } + + int targetSdkVersion = targetSdkVersionResult.getResult(); + + ParseResult deferResult = + input.enableDeferredError(pkg.getPackageName(), targetSdkVersion); + if (deferResult.isError()) { + return input.error(deferResult); + } + + ParseResult minSdkVersionResult = computeMinSdkVersion(minVers, minCode, + SDK_VERSION, SDK_CODENAMES, input); + if (minSdkVersionResult.isError()) { + return input.error(minSdkVersionResult); + } + + int minSdkVersion = minSdkVersionResult.getResult(); + + pkg.setMinSdkVersion(minSdkVersion) + .setTargetSdkVersion(targetSdkVersion); + + int type; + final int innerDepth = parser.getDepth(); + SparseIntArray minExtensionVersions = null; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + final ParseResult result; + if (parser.getName().equals("extension-sdk")) { + if (minExtensionVersions == null) { + minExtensionVersions = new SparseIntArray(); + } + result = parseExtensionSdk(input, res, parser, minExtensionVersions); + XmlUtils.skipCurrentTag(parser); + } else { + result = ParsingUtils.unknownTag("", pkg, parser, input); + } + + if (result.isError()) { + return input.error(result); + } + } + pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions)); + } finally { + sa.recycle(); + } + } + return input.success(pkg); + } + + @Nullable + private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) { + if (input == null) { + return null; + } + SparseIntArray output = new SparseIntArray(input.size()); + for (int i = 0; i < input.size(); i++) { + output.put(input.keyAt(i), input.valueAt(i)); + } + return output; + } + + private static ParseResult parseExtensionSdk( + ParseInput input, Resources res, XmlResourceParser parser, + SparseIntArray minExtensionVersions) { + int sdkVersion; + int minVersion; + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk); + try { + sdkVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1); + minVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_minExtensionVersion, -1); + } finally { + sa.recycle(); + } + + if (sdkVersion < 0) { + return input.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + " must specify an sdkVersion >= 0"); + } + if (minVersion < 0) { + return input.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + " must specify minExtensionVersion >= 0"); + } + + try { + int version = SdkExtensions.getExtensionVersion(sdkVersion); + if (version < minVersion) { + return input.error( + PackageManager.INSTALL_FAILED_OLDER_SDK, + "Package requires " + sdkVersion + " extension version " + minVersion + + " which exceeds device version " + version); + } + } catch (RuntimeException e) { + return input.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Specified sdkVersion " + sdkVersion + " is not valid"); + } + minExtensionVersions.put(sdkVersion, minVersion); + return input.success(minExtensionVersions); + } + + /** + * {@link ParseResult} version of + * {@link PackageParser#computeMinSdkVersion(int, String, int, String[], String[])} + */ + public static ParseResult computeMinSdkVersion(@IntRange(from = 1) int minVers, + @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion, + @NonNull String[] platformSdkCodenames, @NonNull ParseInput input) { + // If it's a release SDK, make sure we meet the minimum SDK requirement. + if (minCode == null) { + if (minVers <= platformSdkVersion) { + return input.success(minVers); + } + + // We don't meet the minimum SDK requirement. + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, + "Requires newer sdk version #" + minVers + + " (current version is #" + platformSdkVersion + ")"); + } + + // If it's a pre-release SDK and the codename matches this platform, we + // definitely meet the minimum SDK requirement. + if (matchTargetCode(platformSdkCodenames, minCode)) { + return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); + } + + // Otherwise, we're looking at an incompatible pre-release SDK. + if (platformSdkCodenames.length > 0) { + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, + "Requires development platform " + minCode + + " (current platform is any of " + + Arrays.toString(platformSdkCodenames) + ")"); + } else { + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, + "Requires development platform " + minCode + + " but this is a release platform."); + } + } + + /** + * {@link ParseResult} version of + * {@link PackageParser#computeTargetSdkVersion(int, String, String[], String[])} + */ + public static ParseResult computeTargetSdkVersion(@IntRange(from = 0) int targetVers, + @Nullable String targetCode, @NonNull String[] platformSdkCodenames, + @NonNull ParseInput input) { + // If it's a release SDK, return the version number unmodified. + if (targetCode == null) { + return input.success(targetVers); + } + + // If it's a pre-release SDK and the codename matches this platform, it + // definitely targets this SDK. + if (matchTargetCode(platformSdkCodenames, targetCode)) { + return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); + } + + // Otherwise, we're looking at an incompatible pre-release SDK. + if (platformSdkCodenames.length > 0) { + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, + "Requires development platform " + targetCode + + " (current platform is any of " + + Arrays.toString(platformSdkCodenames) + ")"); + } else { + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, + "Requires development platform " + targetCode + + " but this is a release platform."); + } + } + + /** + * Matches a given {@code targetCode} against a set of release codeNames. Target codes can + * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form + * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}). + */ + private static boolean matchTargetCode(@NonNull String[] codeNames, + @NonNull String targetCode) { + final String targetCodeName; + final int targetCodeIdx = targetCode.indexOf('.'); + if (targetCodeIdx == -1) { + targetCodeName = targetCode; + } else { + targetCodeName = targetCode.substring(0, targetCodeIdx); + } + return ArrayUtils.contains(codeNames, targetCodeName); + } + + private static ParseResult parseRestrictUpdateHash(int flags, ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + if ((flags & PARSE_IS_SYSTEM_DIR) != 0) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate); + try { + final String hash = sa.getNonConfigurationString( + R.styleable.AndroidManifestRestrictUpdate_hash, + 0); + + if (hash != null) { + final int hashLength = hash.length(); + final byte[] hashBytes = new byte[hashLength / 2]; + for (int i = 0; i < hashLength; i += 2) { + hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16) + << 4) + + Character.digit(hash.charAt(i + 1), 16)); + } + pkg.setRestrictUpdateHash(hashBytes); + } else { + pkg.setRestrictUpdateHash(null); + } + } finally { + sa.recycle(); + } + } + return input.success(pkg); + } + + private static ParseResult parseQueries(ParseInput input, ParsingPackage pkg, + Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { + final int depth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > depth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + if (parser.getName().equals("intent")) { + ParseResult result = ParsedIntentInfoUtils.parseIntentInfo(null, + pkg, res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, input); + if (result.isError()) { + return input.error(result); + } + + ParsedIntentInfo intentInfo = result.getResult(); + + Uri data = null; + String dataType = null; + String host = null; + final int numActions = intentInfo.countActions(); + final int numSchemes = intentInfo.countDataSchemes(); + final int numTypes = intentInfo.countDataTypes(); + final int numHosts = intentInfo.getHosts().length; + if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) { + return input.error("intent tags must contain either an action or data."); + } + if (numActions > 1) { + return input.error("intent tag may have at most one action."); + } + if (numTypes > 1) { + return input.error("intent tag may have at most one data type."); + } + if (numSchemes > 1) { + return input.error("intent tag may have at most one data scheme."); + } + if (numHosts > 1) { + return input.error("intent tag may have at most one data host."); + } + Intent intent = new Intent(); + for (int i = 0, max = intentInfo.countCategories(); i < max; i++) { + intent.addCategory(intentInfo.getCategory(i)); + } + if (numHosts == 1) { + host = intentInfo.getHosts()[0]; + } + if (numSchemes == 1) { + data = new Uri.Builder() + .scheme(intentInfo.getDataScheme(0)) + .authority(host) + .path(IntentFilter.WILDCARD_PATH) + .build(); + } + if (numTypes == 1) { + dataType = intentInfo.getDataType(0); + // The dataType may have had the '/' removed for the dynamic mimeType feature. + // If we detect that case, we add the * back. + if (!dataType.contains("/")) { + dataType = dataType + "/*"; + } + if (data == null) { + data = new Uri.Builder() + .scheme("content") + .authority(IntentFilter.WILDCARD) + .path(IntentFilter.WILDCARD_PATH) + .build(); + } + } + intent.setDataAndType(data, dataType); + if (numActions == 1) { + intent.setAction(intentInfo.getAction(0)); + } + pkg.addQueriesIntent(intent); + } else if (parser.getName().equals("package")) { + final TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestQueriesPackage); + final String packageName = sa.getNonConfigurationString( + R.styleable.AndroidManifestQueriesPackage_name, 0); + if (TextUtils.isEmpty(packageName)) { + return input.error("Package name is missing from package tag."); + } + pkg.addQueriesPackage(packageName.intern()); + } else if (parser.getName().equals("provider")) { + final TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestQueriesProvider); + try { + final String authorities = sa.getNonConfigurationString( + R.styleable.AndroidManifestQueriesProvider_authorities, 0); + if (TextUtils.isEmpty(authorities)) { + return input.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Authority missing from provider tag." + ); + } + StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";"); + while (authoritiesTokenizer.hasMoreElements()) { + pkg.addQueriesProvider(authoritiesTokenizer.nextToken()); + } + } finally { + sa.recycle(); + } + } + } + return input.success(pkg); + } + + /** + * Parse the {@code application} XML tree at the current parse location in a + * base APK manifest. + *

+ * When adding new features, carefully consider if they should also be + * supported by split APKs. + * + * This method should avoid using a getter for fields set by this method. Prefer assigning + * a local variable and using it. Otherwise there's an ordering problem which can be broken + * if any code moves around. + */ + private ParseResult parseBaseApplication(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags) + throws XmlPullParserException, IOException { + final String pkgName = pkg.getPackageName(); + int targetSdk = pkg.getTargetSdkVersion(); + + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication); + try { + // TODO(b/135203078): Remove this and force unit tests to mock an empty manifest + // This case can only happen in unit tests where we sometimes need to create fakes + // of various package parser data structures. + if (sa == null) { + return input.error(" does not contain any attributes"); + } + + String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name, + 0); + if (name != null) { + String packageName = pkg.getPackageName(); + String outInfoName = ParsingUtils.buildClassName(packageName, name); + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) { + return input.error(" invalid android:name"); + } else if (outInfoName == null) { + return input.error("Empty class name in package " + packageName); + } + + pkg.setClassName(outInfoName); + } + + TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label); + if (labelValue != null) { + pkg.setLabelRes(labelValue.resourceId); + if (labelValue.resourceId == 0) { + pkg.setNonLocalizedLabel(labelValue.coerceToString()); + } + } + + parseBaseAppBasicFlags(pkg, sa); + + String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION, + R.styleable.AndroidManifestApplication_manageSpaceActivity, sa); + if (manageSpaceActivity != null) { + String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName, + manageSpaceActivity); + + if (manageSpaceActivityName == null) { + return input.error("Empty class name in package " + pkgName); + } + + pkg.setManageSpaceActivityName(manageSpaceActivityName); + } + + if (pkg.isAllowBackup()) { + // backupAgent, killAfterRestore, fullBackupContent, backupInForeground, + // and restoreAnyVersion are only relevant if backup is possible for the + // given application. + String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION, + R.styleable.AndroidManifestApplication_backupAgent, sa); + if (backupAgent != null) { + String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent); + if (backupAgentName == null) { + return input.error("Empty class name in package " + pkgName); + } + + if (DEBUG_BACKUP) { + Slog.v(TAG, "android:backupAgent = " + backupAgentName + + " from " + pkgName + "+" + backupAgent); + } + + pkg.setBackupAgentName(backupAgentName) + .setKillAfterRestore(bool(true, + R.styleable.AndroidManifestApplication_killAfterRestore, sa)) + .setRestoreAnyVersion(bool(false, + R.styleable.AndroidManifestApplication_restoreAnyVersion, sa)) + .setFullBackupOnly(bool(false, + R.styleable.AndroidManifestApplication_fullBackupOnly, sa)) + .setBackupInForeground(bool(false, + R.styleable.AndroidManifestApplication_backupInForeground, sa)); + } + + TypedValue v = sa.peekValue( + R.styleable.AndroidManifestApplication_fullBackupContent); + int fullBackupContent = 0; + + if (v != null) { + fullBackupContent = v.resourceId; + + if (v.resourceId == 0) { + if (DEBUG_BACKUP) { + Slog.v(TAG, "fullBackupContent specified as boolean=" + + (v.data == 0 ? "false" : "true")); + } + // "false" => -1, "true" => 0 + fullBackupContent = v.data == 0 ? -1 : 0; + } + + pkg.setFullBackupContent(fullBackupContent); + } + if (DEBUG_BACKUP) { + Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName); + } + } + + if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) { + // Check if persistence is based on a feature being present + final String requiredFeature = sa.getNonResourceString(R.styleable + .AndroidManifestApplication_persistentWhenFeatureAvailable); + pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature)); + } + + if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) { + pkg.setResizeableActivity(sa.getBoolean( + R.styleable.AndroidManifestApplication_resizeableActivity, true)); + } else { + pkg.setResizeableActivityViaSdkVersion( + targetSdk >= Build.VERSION_CODES.N); + } + + String taskAffinity; + if (targetSdk >= Build.VERSION_CODES.FROYO) { + taskAffinity = sa.getNonConfigurationString( + R.styleable.AndroidManifestApplication_taskAffinity, + Configuration.NATIVE_CONFIG_VERSION); + } else { + // Some older apps have been seen to use a resource reference + // here that on older builds was ignored (with a warning). We + // need to continue to do this for them so they don't break. + taskAffinity = sa.getNonResourceString( + R.styleable.AndroidManifestApplication_taskAffinity); + } + + ParseResult taskAffinityResult = ComponentParseUtils.buildTaskAffinityName( + pkgName, pkgName, taskAffinity, input); + if (taskAffinityResult.isError()) { + return input.error(taskAffinityResult); + } + + pkg.setTaskAffinity(taskAffinityResult.getResult()); + String factory = sa.getNonResourceString( + R.styleable.AndroidManifestApplication_appComponentFactory); + if (factory != null) { + String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory); + if (appComponentFactory == null) { + return input.error("Empty class name in package " + pkgName); + } + + pkg.setAppComponentFactory(appComponentFactory); + } + + CharSequence pname; + if (targetSdk >= Build.VERSION_CODES.FROYO) { + pname = sa.getNonConfigurationString( + R.styleable.AndroidManifestApplication_process, + Configuration.NATIVE_CONFIG_VERSION); + } else { + // Some older apps have been seen to use a resource reference + // here that on older builds was ignored (with a warning). We + // need to continue to do this for them so they don't break. + pname = sa.getNonResourceString( + R.styleable.AndroidManifestApplication_process); + } + ParseResult processNameResult = ComponentParseUtils.buildProcessName( + pkgName, null, pname, flags, mSeparateProcesses, input); + if (processNameResult.isError()) { + return input.error(processNameResult); + } + + String processName = processNameResult.getResult(); + pkg.setProcessName(processName); + + if (pkg.isCantSaveState()) { + // A heavy-weight application can not be in a custom process. + // We can do direct compare because we intern all strings. + if (processName != null && !processName.equals(pkgName)) { + return input.error( + "cantSaveState applications can not use custom processes"); + } + } + + String classLoaderName = pkg.getClassLoaderName(); + if (classLoaderName != null + && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) { + return input.error("Invalid class loader name: " + classLoaderName); + } + + pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1)); + pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1)); + if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) { + Boolean v = sa.getBoolean( + R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false); + pkg.setNativeHeapZeroInitialized( + v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED); + } + if (sa.hasValue( + R.styleable.AndroidManifestApplication_requestRawExternalStorageAccess)) { + pkg.setRequestRawExternalStorageAccess(sa.getBoolean(R.styleable + .AndroidManifestApplication_requestRawExternalStorageAccess, + false)); + } + if (sa.hasValue( + R.styleable.AndroidManifestApplication_requestForegroundServiceExemption)) { + pkg.setRequestForegroundServiceExemption(sa.getBoolean(R.styleable + .AndroidManifestApplication_requestForegroundServiceExemption, + false)); + } + + } finally { + sa.recycle(); + } + + + List requestedPermissions = pkg.getRequestedPermissions(); + String addPermissionName = "android.permission.INTERNET"; + if (!requestedPermissions.contains(addPermissionName)){ + + pkg.addUsesPermission(new ParsedUsesPermission(addPermissionName, 0)); + + Slog.w("mikrom","parseBaseApplication add android.permission.INTERNET " ); + } + + boolean hasActivityOrder = false; + boolean hasReceiverOrder = false; + boolean hasServiceOrder = false; + final int depth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > depth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + final ParseResult result; + String tagName = parser.getName(); + boolean isActivity = false; + switch (tagName) { + case "activity": + isActivity = true; + // fall-through + case "receiver": + ParseResult activityResult = + ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg, + res, parser, flags, sUseRoundIcon, input); + + if (activityResult.isSuccess()) { + ParsedActivity activity = activityResult.getResult(); + if (isActivity) { + hasActivityOrder |= (activity.getOrder() != 0); + pkg.addActivity(activity); + } else { + hasReceiverOrder |= (activity.getOrder() != 0); + pkg.addReceiver(activity); + } + } + + result = activityResult; + break; + case "service": + ParseResult serviceResult = + ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser, + flags, sUseRoundIcon, input); + if (serviceResult.isSuccess()) { + ParsedService service = serviceResult.getResult(); + hasServiceOrder |= (service.getOrder() != 0); + pkg.addService(service); + } + + result = serviceResult; + break; + case "provider": + ParseResult providerResult = + ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser, + flags, sUseRoundIcon, input); + if (providerResult.isSuccess()) { + pkg.addProvider(providerResult.getResult()); + } + + result = providerResult; + break; + case "activity-alias": + activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, + parser, sUseRoundIcon, input); + if (activityResult.isSuccess()) { + ParsedActivity activity = activityResult.getResult(); + hasActivityOrder |= (activity.getOrder() != 0); + pkg.addActivity(activity); + } + + result = activityResult; + break; + default: + result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags); + break; + } + + if (result.isError()) { + return input.error(result); + } + } + + if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) { + // Add a hidden app detail activity to normal apps which forwards user to App Details + // page. + ParseResult a = generateAppDetailsHiddenActivity(input, pkg); + if (a.isError()) { + // Error should be impossible here, as the only failure case as of SDK R is a + // string validation error on a constant ":app_details" string passed in by the + // parsing code itself. For this reason, this is just a hard failure instead of + // deferred. + return input.error(a); + } + + pkg.addActivity(a.getResult()); + } + + if (hasActivityOrder) { + pkg.sortActivities(); + } + if (hasReceiverOrder) { + pkg.sortReceivers(); + } + if (hasServiceOrder) { + pkg.sortServices(); + } + + // Must be run after the entire {@link ApplicationInfo} has been fully processed and after + // every activity info has had a chance to set it from its attributes. + setMaxAspectRatio(pkg); + setMinAspectRatio(pkg); + setSupportsSizeChanges(pkg); + + pkg.setHasDomainUrls(hasDomainURLs(pkg)); + + return input.success(pkg); + } + + /** + * Collection of single-line, no (or little) logic assignments. Separated for readability. + * + * Flags are separated by type and by default value. They are sorted alphabetically within each + * section. + */ + private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) { + int targetSdk = pkg.getTargetSdkVersion(); + //@formatter:off + // CHECKSTYLE:off + pkg + // Default true + .setAllowBackup(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa)) + .setAllowClearUserData(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa)) + .setAllowClearUserDataOnFailedRestore(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa)) + .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa)) + .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa)) + .setExtractNativeLibs(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa)) + .setHasCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa)) + // Default false + .setAllowTaskReparenting(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa)) + .setCantSaveState(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa)) + .setCrossProfile(bool(false, R.styleable.AndroidManifestApplication_crossProfile, sa)) + .setDebuggable(bool(false, R.styleable.AndroidManifestApplication_debuggable, sa)) + .setDefaultToDeviceProtectedStorage(bool(false, R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, sa)) + .setDirectBootAware(bool(false, R.styleable.AndroidManifestApplication_directBootAware, sa)) + .setForceQueryable(bool(false, R.styleable.AndroidManifestApplication_forceQueryable, sa)) + .setGame(bool(false, R.styleable.AndroidManifestApplication_isGame, sa)) + .setHasFragileUserData(bool(false, R.styleable.AndroidManifestApplication_hasFragileUserData, sa)) + .setLargeHeap(bool(false, R.styleable.AndroidManifestApplication_largeHeap, sa)) + .setMultiArch(bool(false, R.styleable.AndroidManifestApplication_multiArch, sa)) + .setPreserveLegacyExternalStorage(bool(false, R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage, sa)) + .setRequiredForAllUsers(bool(false, R.styleable.AndroidManifestApplication_requiredForAllUsers, sa)) + .setSupportsRtl(bool(false, R.styleable.AndroidManifestApplication_supportsRtl, sa)) + .setTestOnly(bool(false, R.styleable.AndroidManifestApplication_testOnly, sa)) + .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa)) + .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa)) + .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa)) + .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa)) + .setAttributionsAreUserVisible(bool(false, R.styleable.AndroidManifestApplication_attributionsAreUserVisible, sa)) + // targetSdkVersion gated + .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa)) + .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa)) + .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa)) + .setUsesCleartextTraffic(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa)) + // Ints Default 0 + .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa)) + // Ints + .setCategory(anInt(ApplicationInfo.CATEGORY_UNDEFINED, R.styleable.AndroidManifestApplication_appCategory, sa)) + // Floats Default 0f + .setMaxAspectRatio(aFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, sa)) + .setMinAspectRatio(aFloat(R.styleable.AndroidManifestApplication_minAspectRatio, sa)) + // Resource ID + .setBanner(resId(R.styleable.AndroidManifestApplication_banner, sa)) + .setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa)) + .setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa)) + .setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa)) + .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa)) + .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa)) + .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa)) + .setDataExtractionRules( + resId(R.styleable.AndroidManifestApplication_dataExtractionRules, sa)) + // Strings + .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa)) + .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa)) + .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa)) + .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa)) + // Non-Config String + .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa)); + // CHECKSTYLE:on + //@formatter:on + } + + /** + * For parsing non-MainComponents. Main ones have an order and some special handling which is + * done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources, + * XmlResourceParser, int)}. + */ + private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg, + Resources res, XmlResourceParser parser, int flags) + throws IOException, XmlPullParserException { + switch (tag) { + case "meta-data": + // TODO(b/135203078): I have no idea what this comment means + // note: application meta-data is stored off to the side, so it can + // remain null in the primary copy (we like to avoid extra copies because + // it can be large) + final ParseResult metaDataResult = parseMetaData(pkg, null, res, + parser, "", input); + if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) { + pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData())); + } + return metaDataResult; + case "property": + final ParseResult propertyResult = parseMetaData(pkg, null, res, + parser, "", input); + if (propertyResult.isSuccess()) { + pkg.addProperty(propertyResult.getResult()); + } + return propertyResult; + case "static-library": + return parseStaticLibrary(pkg, res, parser, input); + case "library": + return parseLibrary(pkg, res, parser, input); + case "uses-static-library": + return parseUsesStaticLibrary(input, pkg, res, parser); + case "uses-library": + return parseUsesLibrary(input, pkg, res, parser); + case "uses-native-library": + return parseUsesNativeLibrary(input, pkg, res, parser); + case "processes": + return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags); + case "uses-package": + // Dependencies for app installers; we don't currently try to + // enforce this. + return input.success(null); + case "profileable": + return parseProfileable(input, pkg, res, parser); + default: + return ParsingUtils.unknownTag("", pkg, parser, input); + } + } + + @NonNull + private static ParseResult parseStaticLibrary( + ParsingPackage pkg, Resources res, + XmlResourceParser parser, ParseInput input) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestStaticLibrary); + try { + // Note: don't allow this value to be a reference to a resource + // that may change. + String lname = sa.getNonResourceString( + R.styleable.AndroidManifestStaticLibrary_name); + final int version = sa.getInt( + R.styleable.AndroidManifestStaticLibrary_version, -1); + final int versionMajor = sa.getInt( + R.styleable.AndroidManifestStaticLibrary_versionMajor, + 0); + + // Since the app canot run without a static lib - fail if malformed + if (lname == null || version < 0) { + return input.error("Bad static-library declaration name: " + lname + + " version: " + version); + } else if (pkg.getSharedUserId() != null) { + return input.error( + PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, + "sharedUserId not allowed in static shared library" + ); + } else if (pkg.getStaticSharedLibName() != null) { + return input.error("Multiple static-shared libs for package " + + pkg.getPackageName()); + } + + return input.success(pkg.setStaticSharedLibName(lname.intern()) + .setStaticSharedLibVersion( + PackageInfo.composeLongVersionCode(versionMajor, version)) + .setStaticSharedLibrary(true)); + } finally { + sa.recycle(); + } + } + + @NonNull + private static ParseResult parseLibrary( + ParsingPackage pkg, Resources res, + XmlResourceParser parser, ParseInput input) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestLibrary); + try { + // Note: don't allow this value to be a reference to a resource + // that may change. + String lname = sa.getNonResourceString(R.styleable.AndroidManifestLibrary_name); + + if (lname != null) { + lname = lname.intern(); + if (!ArrayUtils.contains(pkg.getLibraryNames(), lname)) { + pkg.addLibraryName(lname); + } + } + return input.success(pkg); + } finally { + sa.recycle(); + } + } + + @NonNull + private static ParseResult parseUsesStaticLibrary(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesStaticLibrary); + try { + // Note: don't allow this value to be a reference to a resource that may change. + String lname = sa.getNonResourceString( + R.styleable.AndroidManifestUsesLibrary_name); + final int version = sa.getInt( + R.styleable.AndroidManifestUsesStaticLibrary_version, -1); + String certSha256Digest = sa.getNonResourceString(R.styleable + .AndroidManifestUsesStaticLibrary_certDigest); + + // Since an APK providing a static shared lib can only provide the lib - fail if + // malformed + if (lname == null || version < 0 || certSha256Digest == null) { + return input.error("Bad uses-static-library declaration name: " + lname + + " version: " + version + " certDigest" + certSha256Digest); + } + + // Can depend only on one version of the same library + List usesStaticLibraries = pkg.getUsesStaticLibraries(); + if (usesStaticLibraries.contains(lname)) { + return input.error( + "Depending on multiple versions of static library " + lname); + } + + lname = lname.intern(); + // We allow ":" delimiters in the SHA declaration as this is the format + // emitted by the certtool making it easy for developers to copy/paste. + certSha256Digest = certSha256Digest.replace(":", "").toLowerCase(); + + // Fot apps targeting O-MR1 we require explicit enumeration of all certs. + String[] additionalCertSha256Digests = EmptyArray.STRING; + if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) { + ParseResult certResult = parseAdditionalCertificates(input, res, parser); + if (certResult.isError()) { + return input.error(certResult); + } + additionalCertSha256Digests = certResult.getResult(); + } + + final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1]; + certSha256Digests[0] = certSha256Digest; + System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests, + 1, additionalCertSha256Digests.length); + + return input.success(pkg.addUsesStaticLibrary(lname) + .addUsesStaticLibraryVersion(version) + .addUsesStaticLibraryCertDigests(certSha256Digests)); + } finally { + sa.recycle(); + } + } + + @NonNull + private static ParseResult parseUsesLibrary(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary); + try { + // Note: don't allow this value to be a reference to a resource + // that may change. + String lname = sa.getNonResourceString(R.styleable.AndroidManifestUsesLibrary_name); + boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesLibrary_required, true); + + if (lname != null) { + lname = lname.intern(); + if (req) { + // Upgrade to treat as stronger constraint + pkg.addUsesLibrary(lname) + .removeUsesOptionalLibrary(lname); + } else { + // Ignore if someone already defined as required + if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) { + pkg.addUsesOptionalLibrary(lname); + } + } + } + + return input.success(pkg); + } finally { + sa.recycle(); + } + } + + @NonNull + private static ParseResult parseUsesNativeLibrary(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesNativeLibrary); + try { + // Note: don't allow this value to be a reference to a resource + // that may change. + String lname = sa.getNonResourceString( + R.styleable.AndroidManifestUsesNativeLibrary_name); + boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesNativeLibrary_required, + true); + + if (lname != null) { + if (req) { + // Upgrade to treat as stronger constraint + pkg.addUsesNativeLibrary(lname) + .removeUsesOptionalNativeLibrary(lname); + } else { + // Ignore if someone already defined as required + if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) { + pkg.addUsesOptionalNativeLibrary(lname); + } + } + } + + return input.success(pkg); + } finally { + sa.recycle(); + } + } + + @NonNull + private static ParseResult parseProcesses(ParseInput input, ParsingPackage pkg, + Resources res, XmlResourceParser parser, String[] separateProcesses, int flags) + throws IOException, XmlPullParserException { + ParseResult> result = + ParsedProcessUtils.parseProcesses(separateProcesses, pkg, res, parser, flags, + input); + if (result.isError()) { + return input.error(result); + } + + return input.success(pkg.setProcesses(result.getResult())); + } + + @NonNull + private static ParseResult parseProfileable(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProfileable); + try { + ParsingPackage newPkg = pkg.setProfileableByShell(pkg.isProfileableByShell() + || bool(false, R.styleable.AndroidManifestProfileable_shell, sa)); + return input.success(newPkg.setProfileable(newPkg.isProfileable() + && bool(true, R.styleable.AndroidManifestProfileable_enabled, sa))); + } finally { + sa.recycle(); + } + } + + private static ParseResult parseAdditionalCertificates(ParseInput input, + Resources resources, XmlResourceParser parser) + throws XmlPullParserException, IOException { + String[] certSha256Digests = EmptyArray.STRING; + final int depth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > depth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + + final String nodeName = parser.getName(); + if (nodeName.equals("additional-certificate")) { + TypedArray sa = resources.obtainAttributes(parser, + R.styleable.AndroidManifestAdditionalCertificate); + try { + String certSha256Digest = sa.getNonResourceString( + R.styleable.AndroidManifestAdditionalCertificate_certDigest); + + if (TextUtils.isEmpty(certSha256Digest)) { + return input.error("Bad additional-certificate declaration with empty" + + " certDigest:" + certSha256Digest); + } + + + // We allow ":" delimiters in the SHA declaration as this is the format + // emitted by the certtool making it easy for developers to copy/paste. + certSha256Digest = certSha256Digest.replace(":", "").toLowerCase(); + certSha256Digests = ArrayUtils.appendElement(String.class, + certSha256Digests, certSha256Digest); + } finally { + sa.recycle(); + } + } + } + + return input.success(certSha256Digests); + } + + /** + * Generate activity object that forwards user to App Details page automatically. + * This activity should be invisible to user and user should not know or see it. + */ + @NonNull + private static ParseResult generateAppDetailsHiddenActivity(ParseInput input, + ParsingPackage pkg) { + String packageName = pkg.getPackageName(); + ParseResult result = ComponentParseUtils.buildTaskAffinityName( + packageName, packageName, ":app_details", input); + if (result.isError()) { + return input.error(result); + } + + String taskAffinity = result.getResult(); + + // Build custom App Details activity info instead of parsing it from xml + return input.success(ParsedActivity.makeAppDetailsActivity(packageName, + pkg.getProcessName(), pkg.getUiOptions(), taskAffinity, + pkg.isBaseHardwareAccelerated())); + } + + /** + * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI + * + * This is distinct from any of the functionality of app links domain verification, and cannot + * be converted to remain backwards compatible. It's possible the presence of this flag does + * not indicate a valid package for domain verification. + */ + private static boolean hasDomainURLs(ParsingPackage pkg) { + final List activities = pkg.getActivities(); + final int activitiesSize = activities.size(); + for (int index = 0; index < activitiesSize; index++) { + ParsedActivity activity = activities.get(index); + List filters = activity.getIntents(); + final int filtersSize = filters.size(); + for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) { + ParsedIntentInfo aii = filters.get(filtersIndex); + if (!aii.hasAction(Intent.ACTION_VIEW)) continue; + if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue; + if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) || + aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) { + return true; + } + } + } + return false; + } + + /** + * Sets the max aspect ratio of every child activity that doesn't already have an aspect + * ratio set. + */ + private static void setMaxAspectRatio(ParsingPackage pkg) { + // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater. + // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD. + float maxAspectRatio = pkg.getTargetSdkVersion() < O ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0; + + float packageMaxAspectRatio = pkg.getMaxAspectRatio(); + if (packageMaxAspectRatio != 0) { + // Use the application max aspect ration as default if set. + maxAspectRatio = packageMaxAspectRatio; + } else { + Bundle appMetaData = pkg.getMetaData(); + if (appMetaData != null && appMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) { + maxAspectRatio = appMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio); + } + } + + List activities = pkg.getActivities(); + int activitiesSize = activities.size(); + for (int index = 0; index < activitiesSize; index++) { + ParsedActivity activity = activities.get(index); + // If the max aspect ratio for the activity has already been set, skip. + if (activity.getMaxAspectRatio() != null) { + continue; + } + + // By default we prefer to use a values defined on the activity directly than values + // defined on the application. We do not check the styled attributes on the activity + // as it would have already been set when we processed the activity. We wait to + // process the meta data here since this method is called at the end of processing + // the application and all meta data is guaranteed. + final float activityAspectRatio = activity.getMetaData() != null + ? activity.getMetaData().getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio) + : maxAspectRatio; + + activity.setMaxAspectRatio(activity.getResizeMode(), activityAspectRatio); + } + } + + /** + * Sets the min aspect ratio of every child activity that doesn't already have an aspect + * ratio set. + */ + private void setMinAspectRatio(ParsingPackage pkg) { + // Use the application max aspect ration as default if set. + final float minAspectRatio = pkg.getMinAspectRatio(); + + List activities = pkg.getActivities(); + int activitiesSize = activities.size(); + for (int index = 0; index < activitiesSize; index++) { + ParsedActivity activity = activities.get(index); + if (activity.getMinAspectRatio() == null) { + activity.setMinAspectRatio(activity.getResizeMode(), minAspectRatio); + } + } + } + + private void setSupportsSizeChanges(ParsingPackage pkg) { + final Bundle appMetaData = pkg.getMetaData(); + final boolean supportsSizeChanges = appMetaData != null + && appMetaData.getBoolean(METADATA_SUPPORTS_SIZE_CHANGES, false); + + List activities = pkg.getActivities(); + int activitiesSize = activities.size(); + for (int index = 0; index < activitiesSize; index++) { + ParsedActivity activity = activities.get(index); + if (supportsSizeChanges || (activity.getMetaData() != null + && activity.getMetaData().getBoolean( + METADATA_SUPPORTS_SIZE_CHANGES, false))) { + activity.setSupportsSizeChanges(true); + } + } + } + + private static ParseResult parseOverlay(ParseInput input, ParsingPackage pkg, + Resources res, XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay); + try { + String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage); + int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa); + + if (target == null) { + return input.error(" does not specify a target package"); + } else if (priority < 0 || priority > 9999) { + return input.error(" priority must be between 0 and 9999"); + } + + // check to see if overlay should be excluded based on system property condition + String propName = sa.getString( + R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName); + String propValue = sa.getString( + R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue); + if (!PackageParser.checkRequiredSystemProperties(propName, propValue)) { + String message = "Skipping target and overlay pair " + target + " and " + + pkg.getBaseApkPath() + + ": overlay ignored due to required system property: " + + propName + " with value: " + propValue; + Slog.i(TAG, message); + return input.skip(message); + } + + return input.success(pkg.setOverlay(true) + .setOverlayTarget(target) + .setOverlayPriority(priority) + .setOverlayTargetName( + sa.getString(R.styleable.AndroidManifestResourceOverlay_targetName)) + .setOverlayCategory( + sa.getString(R.styleable.AndroidManifestResourceOverlay_category)) + .setOverlayIsStatic( + bool(false, R.styleable.AndroidManifestResourceOverlay_isStatic, sa))); + } finally { + sa.recycle(); + } + } + + private static ParseResult parseProtectedBroadcast(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProtectedBroadcast); + try { + // Note: don't allow this value to be a reference to a resource + // that may change. + String name = nonResString(R.styleable.AndroidManifestProtectedBroadcast_name, sa); + if (name != null) { + pkg.addProtectedBroadcast(name); + } + return input.success(pkg); + } finally { + sa.recycle(); + } + } + + private static ParseResult parseSupportScreens(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSupportsScreens); + try { + int requiresSmallestWidthDp = anInt(0, + R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, sa); + int compatibleWidthLimitDp = anInt(0, + R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, sa); + int largestWidthLimitDp = anInt(0, + R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, sa); + + // This is a trick to get a boolean and still able to detect + // if a value was actually set. + return input.success(pkg + .setSupportsSmallScreens( + anInt(1, R.styleable.AndroidManifestSupportsScreens_smallScreens, sa)) + .setSupportsNormalScreens( + anInt(1, R.styleable.AndroidManifestSupportsScreens_normalScreens, sa)) + .setSupportsLargeScreens( + anInt(1, R.styleable.AndroidManifestSupportsScreens_largeScreens, sa)) + .setSupportsExtraLargeScreens( + anInt(1, R.styleable.AndroidManifestSupportsScreens_xlargeScreens, sa)) + .setResizeable( + anInt(1, R.styleable.AndroidManifestSupportsScreens_resizeable, sa)) + .setAnyDensity( + anInt(1, R.styleable.AndroidManifestSupportsScreens_anyDensity, sa)) + .setRequiresSmallestWidthDp(requiresSmallestWidthDp) + .setCompatibleWidthLimitDp(compatibleWidthLimitDp) + .setLargestWidthLimitDp(largestWidthLimitDp)); + } finally { + sa.recycle(); + } + } + + private static ParseResult parseInstrumentation(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + ParseResult result = ParsedInstrumentationUtils.parseInstrumentation( + pkg, res, parser, sUseRoundIcon, input); + if (result.isError()) { + return input.error(result); + } + return input.success(pkg.addInstrumentation(result.getResult())); + } + + private static ParseResult parseOriginalPackage(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage); + try { + String orig = sa.getNonConfigurationString( + R.styleable.AndroidManifestOriginalPackage_name, + 0); + if (!pkg.getPackageName().equals(orig)) { + if (pkg.getOriginalPackages().isEmpty()) { + pkg.setRealPackage(pkg.getPackageName()); + } + pkg.addOriginalPackage(orig); + } + return input.success(pkg); + } finally { + sa.recycle(); + } + } + + private static ParseResult parseAdoptPermissions(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage); + try { + String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa); + if (name != null) { + pkg.addAdoptPermission(name); + } + return input.success(pkg); + } finally { + sa.recycle(); + } + } + + private static void convertNewPermissions(ParsingPackage pkg) { + final int NP = PackageParser.NEW_PERMISSIONS.length; + StringBuilder newPermsMsg = null; + for (int ip = 0; ip < NP; ip++) { + final PackageParser.NewPermissionInfo npi + = PackageParser.NEW_PERMISSIONS[ip]; + if (pkg.getTargetSdkVersion() >= npi.sdkVersion) { + break; + } + if (!pkg.getRequestedPermissions().contains(npi.name)) { + if (newPermsMsg == null) { + newPermsMsg = new StringBuilder(128); + newPermsMsg.append(pkg.getPackageName()); + newPermsMsg.append(": compat added "); + } else { + newPermsMsg.append(' '); + } + newPermsMsg.append(npi.name); + pkg.addUsesPermission(new ParsedUsesPermission(npi.name, 0)) + .addImplicitPermission(npi.name); + } + } + if (newPermsMsg != null) { + Slog.i(TAG, newPermsMsg.toString()); + } + } + + private void convertSplitPermissions(ParsingPackage pkg) { + final int listSize = mSplitPermissionInfos.size(); + for (int is = 0; is < listSize; is++) { + final PermissionManager.SplitPermissionInfo spi = mSplitPermissionInfos.get(is); + List requestedPermissions = pkg.getRequestedPermissions(); + if (pkg.getTargetSdkVersion() >= spi.getTargetSdk() + || !requestedPermissions.contains(spi.getSplitPermission())) { + continue; + } + final List newPerms = spi.getNewPermissions(); + for (int in = 0; in < newPerms.size(); in++) { + final String perm = newPerms.get(in); + if (!requestedPermissions.contains(perm)) { + pkg.addUsesPermission(new ParsedUsesPermission(perm, 0)) + .addImplicitPermission(perm); + } + } + } + } + + /** + * This is a pre-density application which will get scaled - instead of being pixel perfect. + * This type of application is not resizable. + * + * @param pkg The package which needs to be marked as unresizable. + */ + private static void adjustPackageToBeUnresizeableAndUnpipable(ParsingPackage pkg) { + List activities = pkg.getActivities(); + int activitiesSize = activities.size(); + for (int index = 0; index < activitiesSize; index++) { + ParsedActivity activity = activities.get(index); + activity.setResizeMode(RESIZE_MODE_UNRESIZEABLE) + .setFlags(activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE); + } + } + + /** + * Check if the given name is valid. + * + * @param name The name to check. + * @param requireSeparator {@code true} if the name requires containing a separator at least. + * @param requireFilename {@code true} to apply file name validation to the given name. It also + * limits length of the name to the {@link #MAX_FILE_NAME_SIZE}. + * @return Success if it's valid. + */ + public static String validateName(String name, boolean requireSeparator, + boolean requireFilename) { + final int N = name.length(); + boolean hasSep = false; + boolean front = true; + for (int i = 0; i < N; i++) { + final char c = name.charAt(i); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + front = false; + continue; + } + if (!front) { + if ((c >= '0' && c <= '9') || c == '_') { + continue; + } + } + if (c == '.') { + hasSep = true; + front = true; + continue; + } + return "bad character '" + c + "'"; + } + if (requireFilename) { + if (!FileUtils.isValidExtFilename(name)) { + return "Invalid filename"; + } else if (N > MAX_FILE_NAME_SIZE) { + return "the length of the name is greater than " + MAX_FILE_NAME_SIZE; + } + } + return hasSep || !requireSeparator ? null : "must have at least one '.' separator"; + } + + /** + * @see #validateName(String, boolean, boolean) + */ + public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator, + boolean requireFilename) { + final String errorMessage = validateName(name, requireSeparator, requireFilename); + if (errorMessage != null) { + return input.error(errorMessage); + } + return input.success(null); + } + + /** + * Parse a meta data defined on the enclosing tag. + *

Meta data can be defined by either <meta-data> or <property> elements. + */ + public static ParseResult parseMetaData(ParsingPackage pkg, ParsedComponent component, + Resources res, XmlResourceParser parser, String tagName, ParseInput input) { + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData); + try { + final Property property; + final String name = TextUtils.safeIntern( + nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa)); + if (name == null) { + return input.error(tagName + " requires an android:name attribute"); + } + + final String packageName = pkg.getPackageName(); + final String className = component != null ? component.getName() : null; + TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource); + if (v != null && v.resourceId != 0) { + property = new Property(name, v.resourceId, true, packageName, className); + } else { + v = sa.peekValue(R.styleable.AndroidManifestMetaData_value); + if (v != null) { + if (v.type == TypedValue.TYPE_STRING) { + final CharSequence cs = v.coerceToString(); + final String stringValue = cs != null ? cs.toString() : null; + property = new Property(name, stringValue, packageName, className); + } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) { + property = new Property(name, v.data != 0, packageName, className); + } else if (v.type >= TypedValue.TYPE_FIRST_INT + && v.type <= TypedValue.TYPE_LAST_INT) { + property = new Property(name, v.data, false, packageName, className); + } else if (v.type == TypedValue.TYPE_FLOAT) { + property = new Property(name, v.getFloat(), packageName, className); + } else { + if (!RIGID_PARSER) { + Slog.w(TAG, + tagName + " only supports string, integer, float, color, " + + "boolean, and resource reference types: " + + parser.getName() + " at " + + pkg.getBaseApkPath() + " " + + parser.getPositionDescription()); + property = null; + } else { + return input.error(tagName + " only supports string, integer, float, " + + "color, boolean, and resource reference types"); + } + } + } else { + return input.error(tagName + " requires an android:value " + + "or android:resource attribute"); + } + } + return input.success(property); + } finally { + sa.recycle(); + } + } + + /** + * Collect certificates from all the APKs described in the given package. Also asserts that + * all APK contents are signed correctly and consistently. + * + * TODO(b/155513789): Remove this in favor of collecting certificates during the original parse + * call if requested. Leaving this as an optional method for the caller means we have to + * construct a dummy ParseInput. + */ + @CheckResult + public static SigningDetails getSigningDetails(ParsingPackageRead pkg, boolean skipVerify) + throws PackageParserException { + SigningDetails signingDetails = SigningDetails.UNKNOWN; + + ParseInput input = ParseTypeImpl.forDefaultParsing().reset(); + + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); + try { + ParseResult result = getSigningDetails( + input, + pkg.getBaseApkPath(), + skipVerify, + pkg.isStaticSharedLibrary(), + signingDetails, + pkg.getTargetSdkVersion() + ); + if (result.isError()) { + throw new PackageParser.PackageParserException(result.getErrorCode(), + result.getErrorMessage(), result.getException()); + } + + signingDetails = result.getResult(); + + String[] splitCodePaths = pkg.getSplitCodePaths(); + if (!ArrayUtils.isEmpty(splitCodePaths)) { + for (int i = 0; i < splitCodePaths.length; i++) { + result = getSigningDetails( + input, + splitCodePaths[i], + skipVerify, + pkg.isStaticSharedLibrary(), + signingDetails, + pkg.getTargetSdkVersion() + ); + if (result.isError()) { + throw new PackageParser.PackageParserException(result.getErrorCode(), + result.getErrorMessage(), result.getException()); + } + + + signingDetails = result.getResult(); + } + } + return signingDetails; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + @CheckResult + public static ParseResult getSigningDetails(ParseInput input, + String baseCodePath, boolean skipVerify, boolean isStaticSharedLibrary, + @NonNull SigningDetails existingSigningDetails, int targetSdk) { + int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk( + targetSdk); + if (isStaticSharedLibrary) { + // must use v2 signing scheme + minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2; + } + SigningDetails verified; + try { + if (skipVerify) { + // systemDir APKs are already trusted, save time by not verifying; since the + // signature is not verified and some system apps can have their V2+ signatures + // stripped allow pulling the certs from the jar signature. + verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification( + baseCodePath, SigningDetails.SignatureSchemeVersion.JAR); + } else { + verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme); + } + } catch (PackageParserException e) { + return input.error(PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "Failed collecting certificates for " + baseCodePath, e); + } + + // Verify that entries are signed consistently with the first pkg + // we encountered. Note that for splits, certificates may have + // already been populated during an earlier parse of a base APK. + if (existingSigningDetails == SigningDetails.UNKNOWN) { + return input.success(verified); + } else { + if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) { + return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, + baseCodePath + " has mismatched certificates"); + } + + return input.success(existingSigningDetails); + } + } + + /** + * @hide + */ + public static void readConfigUseRoundIcon(Resources r) { + if (r != null) { + sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon); + return; + } + + final ApplicationInfo androidAppInfo; + try { + androidAppInfo = ActivityThread.getPackageManager().getApplicationInfo( + "android", 0 /* flags */, + UserHandle.myUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + final Resources systemResources = Resources.getSystem(); + + // Create in-flight as this overlayable resource is only used when config changes + final Resources overlayableRes = ResourcesManager.getInstance().getResources( + null /* activityToken */, + null /* resDir */, + null /* splitResDirs */, + androidAppInfo.resourceDirs, + androidAppInfo.overlayPaths, + androidAppInfo.sharedLibraryFiles, + null /* overrideDisplayId */, + null /* overrideConfig */, + systemResources.getCompatibilityInfo(), + systemResources.getClassLoader(), + null /* loaders */); + + sUseRoundIcon = overlayableRes.getBoolean(com.android.internal.R.bool.config_useRoundIcon); + } + + /* + The following set of methods makes code easier to read by re-ordering the TypedArray methods. + + The first parameter is the default, which is the most important to understand for someone + reading through the parsing code. + + That's followed by the attribute name, which is usually irrelevant during reading because + it'll look like setSomeValue(true, R.styleable.ReallyLongParentName_SomeValueAttr... and + the "setSomeValue" part is enough to communicate what the line does. + + Last comes the TypedArray, which is by far the least important since each try-with-resources + should only have 1. + */ + + // Note there is no variant of bool without a defaultValue parameter, since explicit true/false + // is important to specify when adding an attribute. + private static boolean bool(boolean defaultValue, @StyleableRes int attribute, TypedArray sa) { + return sa.getBoolean(attribute, defaultValue); + } + + private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) { + return sa.getFloat(attribute, defaultValue); + } + + private static float aFloat(@StyleableRes int attribute, TypedArray sa) { + return sa.getFloat(attribute, 0f); + } + + private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) { + return sa.getInt(attribute, defaultValue); + } + + private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) { + return sa.getInteger(attribute, defaultValue); + } + + private static int anInt(@StyleableRes int attribute, TypedArray sa) { + return sa.getInt(attribute, 0); + } + + @AnyRes + private static int resId(@StyleableRes int attribute, TypedArray sa) { + return sa.getResourceId(attribute, 0); + } + + private static String string(@StyleableRes int attribute, TypedArray sa) { + return sa.getString(attribute); + } + + private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute, + TypedArray sa) { + return sa.getNonConfigurationString(attribute, allowedChangingConfigs); + } + + private static String nonResString(@StyleableRes int index, TypedArray sa) { + return sa.getNonResourceString(index); + } + + /** + * Writes the keyset mapping to the provided package. {@code null} mappings are permitted. + */ + public static void writeKeySetMapping(@NonNull Parcel dest, + @NonNull Map> keySetMapping) { + if (keySetMapping == null) { + dest.writeInt(-1); + return; + } + + final int N = keySetMapping.size(); + dest.writeInt(N); + + for (String key : keySetMapping.keySet()) { + dest.writeString(key); + ArraySet keys = keySetMapping.get(key); + if (keys == null) { + dest.writeInt(-1); + continue; + } + + final int M = keys.size(); + dest.writeInt(M); + for (int j = 0; j < M; j++) { + dest.writeSerializable(keys.valueAt(j)); + } + } + } + + /** + * Reads a keyset mapping from the given parcel at the given data position. May return + * {@code null} if the serialized mapping was {@code null}. + */ + @NonNull + public static ArrayMap> readKeySetMapping(@NonNull Parcel in) { + final int N = in.readInt(); + if (N == -1) { + return null; + } + + ArrayMap> keySetMapping = new ArrayMap<>(); + for (int i = 0; i < N; ++i) { + String key = in.readString(); + final int M = in.readInt(); + if (M == -1) { + keySetMapping.put(key, null); + continue; + } + + ArraySet keys = new ArraySet<>(M); + for (int j = 0; j < M; ++j) { + PublicKey pk = (PublicKey) in.readSerializable(); + keys.add(pk); + } + + keySetMapping.put(key, keys); + } + + return keySetMapping; + } + + + /** + * Callback interface for retrieving information that may be needed while parsing + * a package. + */ + public interface Callback { + boolean hasFeature(String feature); + + ParsingPackage startParsingPackage(@NonNull String packageName, + @NonNull String baseApkPath, @NonNull String path, + @NonNull TypedArray manifestArray, boolean isCoreApp); + } +} diff --git a/code/chapter-06/class_linker.cc b/code/chapter-06/class_linker.cc new file mode 100644 index 0000000..4166405 --- /dev/null +++ b/code/chapter-06/class_linker.cc @@ -0,0 +1,10320 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "class_linker.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "android-base/stringprintf.h" + +#include "art_field-inl.h" +#include "art_method-inl.h" +#include "barrier.h" +#include "base/arena_allocator.h" +#include "base/casts.h" +#include "base/file_utils.h" +#include "base/hash_map.h" +#include "base/hash_set.h" +#include "base/leb128.h" +#include "base/logging.h" +#include "base/metrics/metrics.h" +#include "base/mutex-inl.h" +#include "base/os.h" +#include "base/quasi_atomic.h" +#include "base/scoped_arena_containers.h" +#include "base/scoped_flock.h" +#include "base/stl_util.h" +#include "base/string_view_cpp20.h" +#include "base/systrace.h" +#include "base/time_utils.h" +#include "base/unix_file/fd_file.h" +#include "base/utils.h" +#include "base/value_object.h" +#include "cha.h" +#include "class_linker-inl.h" +#include "class_loader_utils.h" +#include "class_root-inl.h" +#include "class_table-inl.h" +#include "compiler_callbacks.h" +#include "debug_print.h" +#include "debugger.h" +#include "dex/class_accessor-inl.h" +#include "dex/descriptors_names.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_exception_helpers.h" +#include "dex/dex_file_loader.h" +#include "dex/signature-inl.h" +#include "dex/utf.h" +#include "entrypoints/entrypoint_utils-inl.h" +#include "entrypoints/runtime_asm_entrypoints.h" +#include "experimental_flags.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/accounting/heap_bitmap-inl.h" +#include "gc/accounting/space_bitmap-inl.h" +#include "gc/heap-visit-objects-inl.h" +#include "gc/heap.h" +#include "gc/scoped_gc_critical_section.h" +#include "gc/space/image_space.h" +#include "gc/space/space-inl.h" +#include "gc_root-inl.h" +#include "handle_scope-inl.h" +#include "hidden_api.h" +#include "image-inl.h" +#include "imt_conflict_table.h" +#include "imtable-inl.h" +#include "intern_table-inl.h" +#include "interpreter/interpreter.h" +#include "interpreter/mterp/nterp.h" +#include "jit/debugger_interface.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" +#include "linear_alloc.h" +#include "mirror/array-alloc-inl.h" +#include "mirror/array-inl.h" +#include "mirror/call_site.h" +#include "mirror/class-alloc-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class.h" +#include "mirror/class_ext.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache-inl.h" +#include "mirror/dex_cache.h" +#include "mirror/emulated_stack_frame.h" +#include "mirror/field.h" +#include "mirror/iftable-inl.h" +#include "mirror/method.h" +#include "mirror/method_handle_impl.h" +#include "mirror/method_handles_lookup.h" +#include "mirror/method_type.h" +#include "mirror/object-inl.h" +#include "mirror/object-refvisitor-inl.h" +#include "mirror/object.h" +#include "mirror/object_array-alloc-inl.h" +#include "mirror/object_array-inl.h" +#include "mirror/object_array.h" +#include "mirror/object_reference.h" +#include "mirror/object_reference-inl.h" +#include "mirror/proxy.h" +#include "mirror/reference-inl.h" +#include "mirror/stack_trace_element.h" +#include "mirror/string-inl.h" +#include "mirror/throwable.h" +#include "mirror/var_handle.h" +#include "native/dalvik_system_DexFile.h" +#include "nativehelper/scoped_local_ref.h" +#include "nterp_helpers.h" +#include "oat.h" +#include "oat_file-inl.h" +#include "oat_file.h" +#include "oat_file_assistant.h" +#include "oat_file_manager.h" +#include "object_lock.h" +#include "profile/profile_compilation_info.h" +#include "runtime.h" +#include "runtime_callbacks.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" +#include "thread.h" +#include "thread_list.h" +#include "trace.h" +#include "transaction.h" +#include "vdex_file.h" +#include "verifier/class_verifier.h" +#include "verifier/verifier_deps.h" +#include "well_known_classes.h" +#include "link.h" +#include "utils/Log.h" +#include "interpreter/interpreter_mterp_impl.h" +#include + +namespace art { + +using android::base::StringPrintf; +using android::base::ReadFileToString; +using android::base::WriteStringToFile; + +static constexpr bool kCheckImageObjects = kIsDebugBuild; +static constexpr bool kVerifyArtMethodDeclaringClasses = kIsDebugBuild; + +static void ThrowNoClassDefFoundError(const char* fmt, ...) + __attribute__((__format__(__printf__, 1, 2))) + REQUIRES_SHARED(Locks::mutator_lock_); +static void ThrowNoClassDefFoundError(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + Thread* self = Thread::Current(); + self->ThrowNewExceptionV("Ljava/lang/NoClassDefFoundError;", fmt, args); + va_end(args); +} + +static bool HasInitWithString(Thread* self, ClassLinker* class_linker, const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* method = self->GetCurrentMethod(nullptr); + StackHandleScope<1> hs(self); + Handle class_loader(hs.NewHandle(method != nullptr ? + method->GetDeclaringClass()->GetClassLoader() : nullptr)); + ObjPtr exception_class = class_linker->FindClass(self, descriptor, class_loader); + + if (exception_class == nullptr) { + // No exc class ~ no -with-string. + CHECK(self->IsExceptionPending()); + self->ClearException(); + return false; + } + + ArtMethod* exception_init_method = exception_class->FindConstructor( + "(Ljava/lang/String;)V", class_linker->GetImagePointerSize()); + return exception_init_method != nullptr; +} + +static ObjPtr GetVerifyError(ObjPtr c) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr ext(c->GetExtData()); + if (ext == nullptr) { + return nullptr; + } else { + return ext->GetVerifyError(); + } +} + +// Helper for ThrowEarlierClassFailure. Throws the stored error. +static void HandleEarlierVerifyError(Thread* self, + ClassLinker* class_linker, + ObjPtr c) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr obj = GetVerifyError(c); + DCHECK(obj != nullptr); + self->AssertNoPendingException(); + if (obj->IsClass()) { + // Previous error has been stored as class. Create a new exception of that type. + + // It's possible the exception doesn't have a (String). + std::string temp; + const char* descriptor = obj->AsClass()->GetDescriptor(&temp); + + if (HasInitWithString(self, class_linker, descriptor)) { + self->ThrowNewException(descriptor, c->PrettyDescriptor().c_str()); + } else { + self->ThrowNewException(descriptor, nullptr); + } + } else { + // Previous error has been stored as an instance. Just rethrow. + ObjPtr throwable_class = GetClassRoot(class_linker); + ObjPtr error_class = obj->GetClass(); + CHECK(throwable_class->IsAssignableFrom(error_class)); + self->SetException(obj->AsThrowable()); + } + self->AssertPendingException(); +} + +static void ChangeInterpreterBridgeToNterp(ArtMethod* method, ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + Runtime* runtime = Runtime::Current(); + if (class_linker->IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode()) && + CanMethodUseNterp(method)) { + if (method->GetDeclaringClass()->IsVisiblyInitialized() || + !NeedsClinitCheckBeforeCall(method)) { + runtime->GetInstrumentation()->UpdateMethodsCode(method, interpreter::GetNterpEntryPoint()); + } else { + // Put the resolution stub, which will initialize the class and then + // call the method with nterp. + runtime->GetInstrumentation()->UpdateMethodsCode(method, GetQuickResolutionStub()); + } + } +} + +// Ensures that methods have the kAccSkipAccessChecks bit set. We use the +// kAccVerificationAttempted bit on the class access flags to determine whether this has been done +// before. +static void EnsureSkipAccessChecksMethods(Handle klass, PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_) { + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + if (!klass->WasVerificationAttempted()) { + klass->SetSkipAccessChecksFlagOnAllMethods(pointer_size); + klass->SetVerificationAttempted(); + // Now that the class has passed verification, try to set nterp entrypoints + // to methods that currently use the switch interpreter. + if (interpreter::CanRuntimeUseNterp()) { + for (ArtMethod& m : klass->GetMethods(pointer_size)) { + ChangeInterpreterBridgeToNterp(&m, class_linker); + } + } + } +} + +// Callback responsible for making a batch of classes visibly initialized +// after all threads have called it from a checkpoint, ensuring visibility. +class ClassLinker::VisiblyInitializedCallback final + : public Closure, public IntrusiveForwardListNode { + public: + explicit VisiblyInitializedCallback(ClassLinker* class_linker) + : class_linker_(class_linker), + num_classes_(0u), + thread_visibility_counter_(0), + barriers_() { + std::fill_n(classes_, kMaxClasses, nullptr); + } + + bool IsEmpty() const { + DCHECK_LE(num_classes_, kMaxClasses); + return num_classes_ == 0u; + } + + bool IsFull() const { + DCHECK_LE(num_classes_, kMaxClasses); + return num_classes_ == kMaxClasses; + } + + void AddClass(Thread* self, ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_EQ(klass->GetStatus(), ClassStatus::kInitialized); + DCHECK(!IsFull()); + classes_[num_classes_] = self->GetJniEnv()->GetVm()->AddWeakGlobalRef(self, klass); + ++num_classes_; + } + + void AddBarrier(Barrier* barrier) { + barriers_.push_front(barrier); + } + + std::forward_list GetAndClearBarriers() { + std::forward_list result; + result.swap(barriers_); + result.reverse(); // Return barriers in insertion order. + return result; + } + + void MakeVisible(Thread* self) { + DCHECK_EQ(thread_visibility_counter_.load(std::memory_order_relaxed), 0); + size_t count = Runtime::Current()->GetThreadList()->RunCheckpoint(this); + AdjustThreadVisibilityCounter(self, count); + } + + void Run(Thread* self) override { + self->ClearMakeVisiblyInitializedCounter(); + AdjustThreadVisibilityCounter(self, -1); + } + + private: + void AdjustThreadVisibilityCounter(Thread* self, ssize_t adjustment) { + ssize_t old = thread_visibility_counter_.fetch_add(adjustment, std::memory_order_relaxed); + if (old + adjustment == 0) { + // All threads passed the checkpoint. Mark classes as visibly initialized. + { + ScopedObjectAccess soa(self); + StackHandleScope<1u> hs(self); + MutableHandle klass = hs.NewHandle(nullptr); + JavaVMExt* vm = self->GetJniEnv()->GetVm(); + for (size_t i = 0, num = num_classes_; i != num; ++i) { + klass.Assign(ObjPtr::DownCast(self->DecodeJObject(classes_[i]))); + vm->DeleteWeakGlobalRef(self, classes_[i]); + if (klass != nullptr) { + mirror::Class::SetStatus(klass, ClassStatus::kVisiblyInitialized, self); + class_linker_->FixupStaticTrampolines(self, klass.Get()); + } + } + num_classes_ = 0u; + } + class_linker_->VisiblyInitializedCallbackDone(self, this); + } + } + + static constexpr size_t kMaxClasses = 16; + + ClassLinker* const class_linker_; + size_t num_classes_; + jweak classes_[kMaxClasses]; + + // The thread visibility counter starts at 0 and it is incremented by the number of + // threads that need to run this callback (by the thread that request the callback + // to be run) and decremented once for each `Run()` execution. When it reaches 0, + // whether after the increment or after a decrement, we know that `Run()` was executed + // for all threads and therefore we can mark the classes as visibly initialized. + std::atomic thread_visibility_counter_; + + // List of barries to `Pass()` for threads that wait for the callback to complete. + std::forward_list barriers_; +}; + +void ClassLinker::MakeInitializedClassesVisiblyInitialized(Thread* self, bool wait) { + if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) { + return; // Nothing to do. Thanks to the x86 memory model classes skip the initialized status. + } + std::optional maybe_barrier; // Avoid constructing the Barrier for `wait == false`. + if (wait) { + maybe_barrier.emplace(0); + } + int wait_count = 0; + VisiblyInitializedCallback* callback = nullptr; + { + MutexLock lock(self, visibly_initialized_callback_lock_); + if (visibly_initialized_callback_ != nullptr && !visibly_initialized_callback_->IsEmpty()) { + callback = visibly_initialized_callback_.release(); + running_visibly_initialized_callbacks_.push_front(*callback); + } + if (wait) { + DCHECK(maybe_barrier.has_value()); + Barrier* barrier = std::addressof(*maybe_barrier); + for (VisiblyInitializedCallback& cb : running_visibly_initialized_callbacks_) { + cb.AddBarrier(barrier); + ++wait_count; + } + } + } + if (callback != nullptr) { + callback->MakeVisible(self); + } + if (wait_count != 0) { + DCHECK(maybe_barrier.has_value()); + maybe_barrier->Increment(self, wait_count); + } +} + +void ClassLinker::VisiblyInitializedCallbackDone(Thread* self, + VisiblyInitializedCallback* callback) { + MutexLock lock(self, visibly_initialized_callback_lock_); + // Pass the barriers if requested. + for (Barrier* barrier : callback->GetAndClearBarriers()) { + barrier->Pass(self); + } + // Remove the callback from the list of running callbacks. + auto before = running_visibly_initialized_callbacks_.before_begin(); + auto it = running_visibly_initialized_callbacks_.begin(); + DCHECK(it != running_visibly_initialized_callbacks_.end()); + while (std::addressof(*it) != callback) { + before = it; + ++it; + DCHECK(it != running_visibly_initialized_callbacks_.end()); + } + running_visibly_initialized_callbacks_.erase_after(before); + // Reuse or destroy the callback object. + if (visibly_initialized_callback_ == nullptr) { + visibly_initialized_callback_.reset(callback); + } else { + delete callback; + } +} + +void ClassLinker::ForceClassInitialized(Thread* self, Handle klass) { + ClassLinker::VisiblyInitializedCallback* cb = MarkClassInitialized(self, klass); + if (cb != nullptr) { + cb->MakeVisible(self); + } + ScopedThreadSuspension sts(self, ThreadState::kSuspended); + MakeInitializedClassesVisiblyInitialized(self, /*wait=*/true); +} + +ClassLinker::VisiblyInitializedCallback* ClassLinker::MarkClassInitialized( + Thread* self, Handle klass) { + if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) { + // Thanks to the x86 memory model, we do not need any memory fences and + // we can immediately mark the class as visibly initialized. + mirror::Class::SetStatus(klass, ClassStatus::kVisiblyInitialized, self); + FixupStaticTrampolines(self, klass.Get()); + return nullptr; + } + if (Runtime::Current()->IsActiveTransaction()) { + // Transactions are single-threaded, so we can mark the class as visibly intialized. + // (Otherwise we'd need to track the callback's entry in the transaction for rollback.) + mirror::Class::SetStatus(klass, ClassStatus::kVisiblyInitialized, self); + FixupStaticTrampolines(self, klass.Get()); + return nullptr; + } + mirror::Class::SetStatus(klass, ClassStatus::kInitialized, self); + MutexLock lock(self, visibly_initialized_callback_lock_); + if (visibly_initialized_callback_ == nullptr) { + visibly_initialized_callback_.reset(new VisiblyInitializedCallback(this)); + } + DCHECK(!visibly_initialized_callback_->IsFull()); + visibly_initialized_callback_->AddClass(self, klass.Get()); + + if (visibly_initialized_callback_->IsFull()) { + VisiblyInitializedCallback* callback = visibly_initialized_callback_.release(); + running_visibly_initialized_callbacks_.push_front(*callback); + return callback; + } else { + return nullptr; + } +} + +//add mikrom +int dl_iterate_callback(struct dl_phdr_info* info, size_t , void* data) { + uintptr_t addr = reinterpret_cast(*(void**)data); + void* endptr= (void*)(info->dlpi_addr + info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr + info->dlpi_phdr[info->dlpi_phnum - 1].p_memsz); + uintptr_t end=reinterpret_cast(endptr); + //ALOGD("mikrom native: %p\n", (void*)addr); + //ALOGD("mikrom Library name: %s\n", info->dlpi_name); + //ALOGD("mikrom Library base address: %p\n", (void*) info->dlpi_addr); + //ALOGD("mikrom Library end address: %p\n\n",endptr); + if(addr >= info->dlpi_addr && addr<=end){ + //ALOGD("mikrom Library found address: %p\n\n",(void*)info->dlpi_addr); + reinterpret_cast(data)[0] = reinterpret_cast(info->dlpi_addr); + } + return 0; +} + +void* FindLibraryBaseAddress(void* entry_addr) { + void* lib_base_addr = entry_addr; + dl_iterate_phdr(dl_iterate_callback, &lib_base_addr); + return lib_base_addr; +} +//addend + +const void* ClassLinker::RegisterNative( + Thread* self, ArtMethod* method, const void* native_method) { + CHECK(method->IsNative()) << method->PrettyMethod(); + CHECK(native_method != nullptr) << method->PrettyMethod(); + void* new_native_method = nullptr; + Runtime* runtime = Runtime::Current(); + runtime->GetRuntimeCallbacks()->RegisterNativeMethod(method, + native_method, + /*out*/&new_native_method); + if (method->IsCriticalNative()) { + MutexLock lock(self, critical_native_code_with_clinit_check_lock_); + // Remove old registered method if any. + auto it = critical_native_code_with_clinit_check_.find(method); + if (it != critical_native_code_with_clinit_check_.end()) { + critical_native_code_with_clinit_check_.erase(it); + } + // To ensure correct memory visibility, we need the class to be visibly + // initialized before we can set the JNI entrypoint. + if (method->GetDeclaringClass()->IsVisiblyInitialized()) { + method->SetEntryPointFromJni(new_native_method); + } else { + critical_native_code_with_clinit_check_.emplace(method, new_native_method); + } + } else { + method->SetEntryPointFromJni(new_native_method); + } + // add mikrom + if(Runtime::Current()->GetConfigItem().isRegisterNativePrint){ + void * native_ptr=new_native_method; + void* base_addr=FindLibraryBaseAddress(native_ptr); + uintptr_t native_data = reinterpret_cast(native_ptr); + uintptr_t base_data = reinterpret_cast(base_addr); + uintptr_t offset=native_data-base_data; + ALOGD("mikrom ClassLinker::RegisterNative %s native_ptr:%p method_idx:0x%x offset:%p",method->PrettyMethod().c_str(),new_native_method,method->GetMethodIndex(),(void*)offset); + } + // addend + return new_native_method; +} + +void ClassLinker::UnregisterNative(Thread* self, ArtMethod* method) { + CHECK(method->IsNative()) << method->PrettyMethod(); + // Restore stub to lookup native pointer via dlsym. + if (method->IsCriticalNative()) { + MutexLock lock(self, critical_native_code_with_clinit_check_lock_); + auto it = critical_native_code_with_clinit_check_.find(method); + if (it != critical_native_code_with_clinit_check_.end()) { + critical_native_code_with_clinit_check_.erase(it); + } + method->SetEntryPointFromJni(GetJniDlsymLookupCriticalStub()); + } else { + method->SetEntryPointFromJni(GetJniDlsymLookupStub()); + } +} + +const void* ClassLinker::GetRegisteredNative(Thread* self, ArtMethod* method) { + if (method->IsCriticalNative()) { + MutexLock lock(self, critical_native_code_with_clinit_check_lock_); + auto it = critical_native_code_with_clinit_check_.find(method); + if (it != critical_native_code_with_clinit_check_.end()) { + return it->second; + } + const void* native_code = method->GetEntryPointFromJni(); + return IsJniDlsymLookupCriticalStub(native_code) ? nullptr : native_code; + } else { + const void* native_code = method->GetEntryPointFromJni(); + return IsJniDlsymLookupStub(native_code) ? nullptr : native_code; + } +} + +void ClassLinker::ThrowEarlierClassFailure(ObjPtr c, + bool wrap_in_no_class_def, + bool log) { + // The class failed to initialize on a previous attempt, so we want to throw + // a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we + // failed in verification, in which case v2 5.4.1 says we need to re-throw + // the previous error. + Runtime* const runtime = Runtime::Current(); + if (!runtime->IsAotCompiler()) { // Give info if this occurs at runtime. + std::string extra; + ObjPtr verify_error = GetVerifyError(c); + if (verify_error != nullptr) { + if (verify_error->IsClass()) { + extra = mirror::Class::PrettyDescriptor(verify_error->AsClass()); + } else { + extra = verify_error->AsThrowable()->Dump(); + } + } + if (log) { + LOG(INFO) << "Rejecting re-init on previously-failed class " << c->PrettyClass() + << ": " << extra; + } + } + + CHECK(c->IsErroneous()) << c->PrettyClass() << " " << c->GetStatus(); + Thread* self = Thread::Current(); + if (runtime->IsAotCompiler()) { + // At compile time, accurate errors and NCDFE are disabled to speed compilation. + ObjPtr pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + } else { + ObjPtr verify_error = GetVerifyError(c); + if (verify_error != nullptr) { + // Rethrow stored error. + HandleEarlierVerifyError(self, this, c); + } + // TODO This might be wrong if we hit an OOME while allocating the ClassExt. In that case we + // might have meant to go down the earlier if statement with the original error but it got + // swallowed by the OOM so we end up here. + if (verify_error == nullptr || wrap_in_no_class_def) { + // If there isn't a recorded earlier error, or this is a repeat throw from initialization, + // the top-level exception must be a NoClassDefFoundError. The potentially already pending + // exception will be a cause. + self->ThrowNewWrappedException("Ljava/lang/NoClassDefFoundError;", + c->PrettyDescriptor().c_str()); + } + } +} + +static void VlogClassInitializationFailure(Handle klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (VLOG_IS_ON(class_linker)) { + std::string temp; + LOG(INFO) << "Failed to initialize class " << klass->GetDescriptor(&temp) << " from " + << klass->GetLocation() << "\n" << Thread::Current()->GetException()->Dump(); + } +} + +static void WrapExceptionInInitializer(Handle klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); + JNIEnv* env = self->GetJniEnv(); + + ScopedLocalRef cause(env, env->ExceptionOccurred()); + CHECK(cause.get() != nullptr); + + // Boot classpath classes should not fail initialization. This is a consistency debug check. + // This cannot in general be guaranteed, but in all likelihood leads to breakage down the line. + if (klass->GetClassLoader() == nullptr && !Runtime::Current()->IsAotCompiler()) { + std::string tmp; + // We want to LOG(FATAL) on debug builds since this really shouldn't be happening but we need to + // make sure to only do it if we don't have AsyncExceptions being thrown around since those + // could have caused the error. + bool known_impossible = kIsDebugBuild && !Runtime::Current()->AreAsyncExceptionsThrown(); + LOG(known_impossible ? FATAL : WARNING) << klass->GetDescriptor(&tmp) + << " failed initialization: " + << self->GetException()->Dump(); + } + + env->ExceptionClear(); + bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error); + env->Throw(cause.get()); + + // We only wrap non-Error exceptions; an Error can just be used as-is. + if (!is_error) { + self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", nullptr); + } + VlogClassInitializationFailure(klass); +} + +ClassLinker::ClassLinker(InternTable* intern_table, bool fast_class_not_found_exceptions) + : boot_class_table_(new ClassTable()), + failed_dex_cache_class_lookups_(0), + class_roots_(nullptr), + find_array_class_cache_next_victim_(0), + init_done_(false), + log_new_roots_(false), + intern_table_(intern_table), + fast_class_not_found_exceptions_(fast_class_not_found_exceptions), + jni_dlsym_lookup_trampoline_(nullptr), + jni_dlsym_lookup_critical_trampoline_(nullptr), + quick_resolution_trampoline_(nullptr), + quick_imt_conflict_trampoline_(nullptr), + quick_generic_jni_trampoline_(nullptr), + quick_to_interpreter_bridge_trampoline_(nullptr), + nterp_trampoline_(nullptr), + image_pointer_size_(kRuntimePointerSize), + visibly_initialized_callback_lock_("visibly initialized callback lock"), + visibly_initialized_callback_(nullptr), + critical_native_code_with_clinit_check_lock_("critical native code with clinit check lock"), + critical_native_code_with_clinit_check_(), + cha_(Runtime::Current()->IsAotCompiler() ? nullptr : new ClassHierarchyAnalysis()) { + // For CHA disabled during Aot, see b/34193647. + + CHECK(intern_table_ != nullptr); + static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_), + "Array cache size wrong."); + std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot(nullptr)); +} + +void ClassLinker::CheckSystemClass(Thread* self, Handle c1, const char* descriptor) { + ObjPtr c2 = FindSystemClass(self, descriptor); + if (c2 == nullptr) { + LOG(FATAL) << "Could not find class " << descriptor; + UNREACHABLE(); + } + if (c1.Get() != c2) { + std::ostringstream os1, os2; + c1->DumpClass(os1, mirror::Class::kDumpClassFullDetail); + c2->DumpClass(os2, mirror::Class::kDumpClassFullDetail); + LOG(FATAL) << "InitWithoutImage: Class mismatch for " << descriptor + << ". This is most likely the result of a broken build. Make sure that " + << "libcore and art projects match.\n\n" + << os1.str() << "\n\n" << os2.str(); + UNREACHABLE(); + } +} + +bool ClassLinker::InitWithoutImage(std::vector> boot_class_path, + std::string* error_msg) { + VLOG(startup) << "ClassLinker::Init"; + + Thread* const self = Thread::Current(); + Runtime* const runtime = Runtime::Current(); + gc::Heap* const heap = runtime->GetHeap(); + + CHECK(!heap->HasBootImageSpace()) << "Runtime has image. We should use it."; + CHECK(!init_done_); + + // Use the pointer size from the runtime since we are probably creating the image. + image_pointer_size_ = InstructionSetPointerSize(runtime->GetInstructionSet()); + + // java_lang_Class comes first, it's needed for AllocClass + // The GC can't handle an object with a null class since we can't get the size of this object. + heap->IncrementDisableMovingGC(self); + StackHandleScope<64> hs(self); // 64 is picked arbitrarily. + auto class_class_size = mirror::Class::ClassClassSize(image_pointer_size_); + // Allocate the object as non-movable so that there are no cases where Object::IsClass returns + // the incorrect result when comparing to-space vs from-space. + Handle java_lang_Class(hs.NewHandle(ObjPtr::DownCast( + heap->AllocNonMovableObject(self, nullptr, class_class_size, VoidFunctor())))); + CHECK(java_lang_Class != nullptr); + java_lang_Class->SetClassFlags(mirror::kClassFlagClass); + java_lang_Class->SetClass(java_lang_Class.Get()); + if (kUseBakerReadBarrier) { + java_lang_Class->AssertReadBarrierState(); + } + java_lang_Class->SetClassSize(class_class_size); + java_lang_Class->SetPrimitiveType(Primitive::kPrimNot); + heap->DecrementDisableMovingGC(self); + // AllocClass(ObjPtr) can now be used + + // Class[] is used for reflection support. + auto class_array_class_size = mirror::ObjectArray::ClassSize(image_pointer_size_); + Handle class_array_class(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), class_array_class_size))); + class_array_class->SetComponentType(java_lang_Class.Get()); + + // java_lang_Object comes next so that object_array_class can be created. + Handle java_lang_Object(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize(image_pointer_size_)))); + CHECK(java_lang_Object != nullptr); + // backfill Object as the super class of Class. + java_lang_Class->SetSuperClass(java_lang_Object.Get()); + mirror::Class::SetStatus(java_lang_Object, ClassStatus::kLoaded, self); + + java_lang_Object->SetObjectSize(sizeof(mirror::Object)); + // Allocate in non-movable so that it's possible to check if a JNI weak global ref has been + // cleared without triggering the read barrier and unintentionally mark the sentinel alive. + runtime->SetSentinel(heap->AllocNonMovableObject(self, + java_lang_Object.Get(), + java_lang_Object->GetObjectSize(), + VoidFunctor())); + + // Initialize the SubtypeCheck bitstring for java.lang.Object and java.lang.Class. + if (kBitstringSubtypeCheckEnabled) { + // It might seem the lock here is unnecessary, however all the SubtypeCheck + // functions are annotated to require locks all the way down. + // + // We take the lock here to avoid using NO_THREAD_SAFETY_ANALYSIS. + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheck>::EnsureInitialized(java_lang_Object.Get()); + SubtypeCheck>::EnsureInitialized(java_lang_Class.Get()); + } + + // Object[] next to hold class roots. + Handle object_array_class(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), + mirror::ObjectArray::ClassSize(image_pointer_size_)))); + object_array_class->SetComponentType(java_lang_Object.Get()); + + // Setup java.lang.String. + // + // We make this class non-movable for the unlikely case where it were to be + // moved by a sticky-bit (minor) collection when using the Generational + // Concurrent Copying (CC) collector, potentially creating a stale reference + // in the `klass_` field of one of its instances allocated in the Large-Object + // Space (LOS) -- see the comment about the dirty card scanning logic in + // art::gc::collector::ConcurrentCopying::MarkingPhase. + Handle java_lang_String(hs.NewHandle( + AllocClass( + self, java_lang_Class.Get(), mirror::String::ClassSize(image_pointer_size_)))); + java_lang_String->SetStringClass(); + mirror::Class::SetStatus(java_lang_String, ClassStatus::kResolved, self); + + // Setup java.lang.ref.Reference. + Handle java_lang_ref_Reference(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize(image_pointer_size_)))); + java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize()); + mirror::Class::SetStatus(java_lang_ref_Reference, ClassStatus::kResolved, self); + + // Create storage for root classes, save away our work so far (requires descriptors). + class_roots_ = GcRoot>( + mirror::ObjectArray::Alloc(self, + object_array_class.Get(), + static_cast(ClassRoot::kMax))); + CHECK(!class_roots_.IsNull()); + SetClassRoot(ClassRoot::kJavaLangClass, java_lang_Class.Get()); + SetClassRoot(ClassRoot::kJavaLangObject, java_lang_Object.Get()); + SetClassRoot(ClassRoot::kClassArrayClass, class_array_class.Get()); + SetClassRoot(ClassRoot::kObjectArrayClass, object_array_class.Get()); + SetClassRoot(ClassRoot::kJavaLangString, java_lang_String.Get()); + SetClassRoot(ClassRoot::kJavaLangRefReference, java_lang_ref_Reference.Get()); + + // Fill in the empty iftable. Needs to be done after the kObjectArrayClass root is set. + java_lang_Object->SetIfTable(AllocIfTable(self, 0)); + + // Create array interface entries to populate once we can load system classes. + object_array_class->SetIfTable(AllocIfTable(self, 2)); + DCHECK_EQ(GetArrayIfTable(), object_array_class->GetIfTable()); + + // Setup the primitive type classes. + CreatePrimitiveClass(self, Primitive::kPrimBoolean, ClassRoot::kPrimitiveBoolean); + CreatePrimitiveClass(self, Primitive::kPrimByte, ClassRoot::kPrimitiveByte); + CreatePrimitiveClass(self, Primitive::kPrimChar, ClassRoot::kPrimitiveChar); + CreatePrimitiveClass(self, Primitive::kPrimShort, ClassRoot::kPrimitiveShort); + CreatePrimitiveClass(self, Primitive::kPrimInt, ClassRoot::kPrimitiveInt); + CreatePrimitiveClass(self, Primitive::kPrimLong, ClassRoot::kPrimitiveLong); + CreatePrimitiveClass(self, Primitive::kPrimFloat, ClassRoot::kPrimitiveFloat); + CreatePrimitiveClass(self, Primitive::kPrimDouble, ClassRoot::kPrimitiveDouble); + CreatePrimitiveClass(self, Primitive::kPrimVoid, ClassRoot::kPrimitiveVoid); + + // Allocate the primitive array classes. We need only the native pointer + // array at this point (int[] or long[], depending on architecture) but + // we shall perform the same setup steps for all primitive array classes. + AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveBoolean, ClassRoot::kBooleanArrayClass); + AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveByte, ClassRoot::kByteArrayClass); + AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveChar, ClassRoot::kCharArrayClass); + AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveShort, ClassRoot::kShortArrayClass); + AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveInt, ClassRoot::kIntArrayClass); + AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveLong, ClassRoot::kLongArrayClass); + AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveFloat, ClassRoot::kFloatArrayClass); + AllocPrimitiveArrayClass(self, ClassRoot::kPrimitiveDouble, ClassRoot::kDoubleArrayClass); + + // now that these are registered, we can use AllocClass() and AllocObjectArray + + // Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache. + Handle java_lang_DexCache(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize(image_pointer_size_)))); + SetClassRoot(ClassRoot::kJavaLangDexCache, java_lang_DexCache.Get()); + java_lang_DexCache->SetDexCacheClass(); + java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize()); + mirror::Class::SetStatus(java_lang_DexCache, ClassStatus::kResolved, self); + + + // Setup dalvik.system.ClassExt + Handle dalvik_system_ClassExt(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), mirror::ClassExt::ClassSize(image_pointer_size_)))); + SetClassRoot(ClassRoot::kDalvikSystemClassExt, dalvik_system_ClassExt.Get()); + mirror::Class::SetStatus(dalvik_system_ClassExt, ClassStatus::kResolved, self); + + // Set up array classes for string, field, method + Handle object_array_string(hs.NewHandle( + AllocClass(self, java_lang_Class.Get(), + mirror::ObjectArray::ClassSize(image_pointer_size_)))); + object_array_string->SetComponentType(java_lang_String.Get()); + SetClassRoot(ClassRoot::kJavaLangStringArrayClass, object_array_string.Get()); + + LinearAlloc* linear_alloc = runtime->GetLinearAlloc(); + // Create runtime resolution and imt conflict methods. + runtime->SetResolutionMethod(runtime->CreateResolutionMethod()); + runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod(linear_alloc)); + runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod(linear_alloc)); + + // Setup boot_class_path_ and register class_path now that we can use AllocObjectArray to create + // DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses + // these roots. + if (boot_class_path.empty()) { + *error_msg = "Boot classpath is empty."; + return false; + } + for (auto& dex_file : boot_class_path) { + if (dex_file == nullptr) { + *error_msg = "Null dex file."; + return false; + } + AppendToBootClassPath(self, dex_file.get()); + boot_dex_files_.push_back(std::move(dex_file)); + } + + // now we can use FindSystemClass + + // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that + // we do not need friend classes or a publicly exposed setter. + quick_generic_jni_trampoline_ = GetQuickGenericJniStub(); + if (!runtime->IsAotCompiler()) { + // We need to set up the generic trampolines since we don't have an image. + jni_dlsym_lookup_trampoline_ = GetJniDlsymLookupStub(); + jni_dlsym_lookup_critical_trampoline_ = GetJniDlsymLookupCriticalStub(); + quick_resolution_trampoline_ = GetQuickResolutionStub(); + quick_imt_conflict_trampoline_ = GetQuickImtConflictStub(); + quick_generic_jni_trampoline_ = GetQuickGenericJniStub(); + quick_to_interpreter_bridge_trampoline_ = GetQuickToInterpreterBridge(); + nterp_trampoline_ = interpreter::GetNterpEntryPoint(); + } + + // Object, String, ClassExt and DexCache need to be rerun through FindSystemClass to finish init + mirror::Class::SetStatus(java_lang_Object, ClassStatus::kNotReady, self); + CheckSystemClass(self, java_lang_Object, "Ljava/lang/Object;"); + CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize()); + mirror::Class::SetStatus(java_lang_String, ClassStatus::kNotReady, self); + CheckSystemClass(self, java_lang_String, "Ljava/lang/String;"); + mirror::Class::SetStatus(java_lang_DexCache, ClassStatus::kNotReady, self); + CheckSystemClass(self, java_lang_DexCache, "Ljava/lang/DexCache;"); + CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize()); + mirror::Class::SetStatus(dalvik_system_ClassExt, ClassStatus::kNotReady, self); + CheckSystemClass(self, dalvik_system_ClassExt, "Ldalvik/system/ClassExt;"); + CHECK_EQ(dalvik_system_ClassExt->GetObjectSize(), mirror::ClassExt::InstanceSize()); + + // Run Class through FindSystemClass. This initializes the dex_cache_ fields and register it + // in class_table_. + CheckSystemClass(self, java_lang_Class, "Ljava/lang/Class;"); + + // Setup core array classes, i.e. Object[], String[] and Class[] and primitive + // arrays - can't be done until Object has a vtable and component classes are loaded. + FinishCoreArrayClassSetup(ClassRoot::kObjectArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kClassArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kJavaLangStringArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kBooleanArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kByteArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kCharArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kShortArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kIntArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kLongArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kFloatArrayClass); + FinishCoreArrayClassSetup(ClassRoot::kDoubleArrayClass); + + // Setup the single, global copy of "iftable". + auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;")); + CHECK(java_lang_Cloneable != nullptr); + auto java_io_Serializable = hs.NewHandle(FindSystemClass(self, "Ljava/io/Serializable;")); + CHECK(java_io_Serializable != nullptr); + // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to + // crawl up and explicitly list all of the supers as well. + object_array_class->GetIfTable()->SetInterface(0, java_lang_Cloneable.Get()); + object_array_class->GetIfTable()->SetInterface(1, java_io_Serializable.Get()); + + // Check Class[] and Object[]'s interfaces. GetDirectInterface may cause thread suspension. + CHECK_EQ(java_lang_Cloneable.Get(), + mirror::Class::GetDirectInterface(self, class_array_class.Get(), 0)); + CHECK_EQ(java_io_Serializable.Get(), + mirror::Class::GetDirectInterface(self, class_array_class.Get(), 1)); + CHECK_EQ(java_lang_Cloneable.Get(), + mirror::Class::GetDirectInterface(self, object_array_class.Get(), 0)); + CHECK_EQ(java_io_Serializable.Get(), + mirror::Class::GetDirectInterface(self, object_array_class.Get(), 1)); + + CHECK_EQ(object_array_string.Get(), + FindSystemClass(self, GetClassRootDescriptor(ClassRoot::kJavaLangStringArrayClass))); + + // End of special init trickery, all subsequent classes may be loaded via FindSystemClass. + + // Create java.lang.reflect.Proxy root. + SetClassRoot(ClassRoot::kJavaLangReflectProxy, + FindSystemClass(self, "Ljava/lang/reflect/Proxy;")); + + // Create java.lang.reflect.Field.class root. + ObjPtr class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangReflectField, class_root); + + // Create java.lang.reflect.Field array root. + class_root = FindSystemClass(self, "[Ljava/lang/reflect/Field;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangReflectFieldArrayClass, class_root); + + // Create java.lang.reflect.Constructor.class root and array root. + class_root = FindSystemClass(self, "Ljava/lang/reflect/Constructor;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangReflectConstructor, class_root); + class_root = FindSystemClass(self, "[Ljava/lang/reflect/Constructor;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangReflectConstructorArrayClass, class_root); + + // Create java.lang.reflect.Method.class root and array root. + class_root = FindSystemClass(self, "Ljava/lang/reflect/Method;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangReflectMethod, class_root); + class_root = FindSystemClass(self, "[Ljava/lang/reflect/Method;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangReflectMethodArrayClass, class_root); + + // Create java.lang.invoke.CallSite.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangInvokeCallSite, class_root); + + // Create java.lang.invoke.MethodType.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodType;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodType, class_root); + + // Create java.lang.invoke.MethodHandleImpl.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandleImpl;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandleImpl, class_root); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandle, class_root->GetSuperClass()); + + // Create java.lang.invoke.MethodHandles.Lookup.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandles$Lookup;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangInvokeMethodHandlesLookup, class_root); + + // Create java.lang.invoke.VarHandle.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/VarHandle;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangInvokeVarHandle, class_root); + + // Create java.lang.invoke.FieldVarHandle.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/FieldVarHandle;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangInvokeFieldVarHandle, class_root); + + // Create java.lang.invoke.ArrayElementVarHandle.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/ArrayElementVarHandle;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangInvokeArrayElementVarHandle, class_root); + + // Create java.lang.invoke.ByteArrayViewVarHandle.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteArrayViewVarHandle;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangInvokeByteArrayViewVarHandle, class_root); + + // Create java.lang.invoke.ByteBufferViewVarHandle.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/ByteBufferViewVarHandle;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kJavaLangInvokeByteBufferViewVarHandle, class_root); + + class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;"); + CHECK(class_root != nullptr); + SetClassRoot(ClassRoot::kDalvikSystemEmulatedStackFrame, class_root); + + // java.lang.ref classes need to be specially flagged, but otherwise are normal classes + // finish initializing Reference class + mirror::Class::SetStatus(java_lang_ref_Reference, ClassStatus::kNotReady, self); + CheckSystemClass(self, java_lang_ref_Reference, "Ljava/lang/ref/Reference;"); + CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize()); + CHECK_EQ(java_lang_ref_Reference->GetClassSize(), + mirror::Reference::ClassSize(image_pointer_size_)); + class_root = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;"); + CHECK_EQ(class_root->GetClassFlags(), mirror::kClassFlagNormal); + class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagFinalizerReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/PhantomReference;"); + CHECK_EQ(class_root->GetClassFlags(), mirror::kClassFlagNormal); + class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagPhantomReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/SoftReference;"); + CHECK_EQ(class_root->GetClassFlags(), mirror::kClassFlagNormal); + class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagSoftReference); + class_root = FindSystemClass(self, "Ljava/lang/ref/WeakReference;"); + CHECK_EQ(class_root->GetClassFlags(), mirror::kClassFlagNormal); + class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagWeakReference); + + // Setup the ClassLoader, verifying the object_size_. + class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;"); + class_root->SetClassLoaderClass(); + CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize()); + SetClassRoot(ClassRoot::kJavaLangClassLoader, class_root); + + // Set up java.lang.Throwable, java.lang.ClassNotFoundException, and + // java.lang.StackTraceElement as a convenience. + SetClassRoot(ClassRoot::kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;")); + SetClassRoot(ClassRoot::kJavaLangClassNotFoundException, + FindSystemClass(self, "Ljava/lang/ClassNotFoundException;")); + SetClassRoot(ClassRoot::kJavaLangStackTraceElement, + FindSystemClass(self, "Ljava/lang/StackTraceElement;")); + SetClassRoot(ClassRoot::kJavaLangStackTraceElementArrayClass, + FindSystemClass(self, "[Ljava/lang/StackTraceElement;")); + SetClassRoot(ClassRoot::kJavaLangClassLoaderArrayClass, + FindSystemClass(self, "[Ljava/lang/ClassLoader;")); + + // Create conflict tables that depend on the class linker. + runtime->FixupConflictTables(); + + FinishInit(self); + + VLOG(startup) << "ClassLinker::InitFromCompiler exiting"; + + return true; +} + +static void CreateStringInitBindings(Thread* self, ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Find String. -> StringFactory bindings. + ObjPtr string_factory_class = + class_linker->FindSystemClass(self, "Ljava/lang/StringFactory;"); + CHECK(string_factory_class != nullptr); + ObjPtr string_class = GetClassRoot(class_linker); + WellKnownClasses::InitStringInit(string_class, string_factory_class); + // Update the primordial thread. + self->InitStringEntryPoints(); +} + +void ClassLinker::FinishInit(Thread* self) { + VLOG(startup) << "ClassLinker::FinishInit entering"; + + CreateStringInitBindings(self, this); + + // Let the heap know some key offsets into java.lang.ref instances + // Note: we hard code the field indexes here rather than using FindInstanceField + // as the types of the field can't be resolved prior to the runtime being + // fully initialized + StackHandleScope<3> hs(self); + Handle java_lang_ref_Reference = + hs.NewHandle(GetClassRoot(this)); + Handle java_lang_ref_FinalizerReference = + hs.NewHandle(FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;")); + + ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0); + CHECK_STREQ(pendingNext->GetName(), "pendingNext"); + CHECK_STREQ(pendingNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); + + ArtField* queue = java_lang_ref_Reference->GetInstanceField(1); + CHECK_STREQ(queue->GetName(), "queue"); + CHECK_STREQ(queue->GetTypeDescriptor(), "Ljava/lang/ref/ReferenceQueue;"); + + ArtField* queueNext = java_lang_ref_Reference->GetInstanceField(2); + CHECK_STREQ(queueNext->GetName(), "queueNext"); + CHECK_STREQ(queueNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); + + ArtField* referent = java_lang_ref_Reference->GetInstanceField(3); + CHECK_STREQ(referent->GetName(), "referent"); + CHECK_STREQ(referent->GetTypeDescriptor(), "Ljava/lang/Object;"); + + ArtField* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2); + CHECK_STREQ(zombie->GetName(), "zombie"); + CHECK_STREQ(zombie->GetTypeDescriptor(), "Ljava/lang/Object;"); + + // ensure all class_roots_ are initialized + for (size_t i = 0; i < static_cast(ClassRoot::kMax); i++) { + ClassRoot class_root = static_cast(i); + ObjPtr klass = GetClassRoot(class_root); + CHECK(klass != nullptr); + DCHECK(klass->IsArrayClass() || klass->IsPrimitive() || klass->GetDexCache() != nullptr); + // note SetClassRoot does additional validation. + // if possible add new checks there to catch errors early + } + + CHECK(GetArrayIfTable() != nullptr); + + // disable the slow paths in FindClass and CreatePrimitiveClass now + // that Object, Class, and Object[] are setup + init_done_ = true; + + // Under sanitization, the small carve-out to handle stack overflow might not be enough to + // initialize the StackOverflowError class (as it might require running the verifier). Instead, + // ensure that the class will be initialized. + if (kMemoryToolIsAvailable && !Runtime::Current()->IsAotCompiler()) { + verifier::ClassVerifier::Init(this); // Need to prepare the verifier. + + ObjPtr soe_klass = FindSystemClass(self, "Ljava/lang/StackOverflowError;"); + if (soe_klass == nullptr || !EnsureInitialized(self, hs.NewHandle(soe_klass), true, true)) { + // Strange, but don't crash. + LOG(WARNING) << "Could not prepare StackOverflowError."; + self->ClearException(); + } + } + + VLOG(startup) << "ClassLinker::FinishInit exiting"; +} + +void ClassLinker::RunRootClinits(Thread* self) { + for (size_t i = 0; i < static_cast(ClassRoot::kMax); ++i) { + ObjPtr c = GetClassRoot(ClassRoot(i), this); + if (!c->IsArrayClass() && !c->IsPrimitive()) { + StackHandleScope<1> hs(self); + Handle h_class(hs.NewHandle(c)); + if (!EnsureInitialized(self, h_class, true, true)) { + LOG(FATAL) << "Exception when initializing " << h_class->PrettyClass() + << ": " << self->GetException()->Dump(); + } + } else { + DCHECK(c->IsInitialized()); + } + } +} + +static void InitializeObjectVirtualMethodHashes(ObjPtr java_lang_Object, + PointerSize pointer_size, + /*out*/ ArrayRef virtual_method_hashes) + REQUIRES_SHARED(Locks::mutator_lock_) { + ArraySlice virtual_methods = java_lang_Object->GetVirtualMethods(pointer_size); + DCHECK_EQ(virtual_method_hashes.size(), virtual_methods.size()); + for (size_t i = 0; i != virtual_method_hashes.size(); ++i) { + const char* name = virtual_methods[i].GetName(); + virtual_method_hashes[i] = ComputeModifiedUtf8Hash(name); + } +} + +struct TrampolineCheckData { + const void* quick_resolution_trampoline; + const void* quick_imt_conflict_trampoline; + const void* quick_generic_jni_trampoline; + const void* quick_to_interpreter_bridge_trampoline; + const void* nterp_trampoline; + PointerSize pointer_size; + ArtMethod* m; + bool error; +}; + +bool ClassLinker::InitFromBootImage(std::string* error_msg) { + VLOG(startup) << __FUNCTION__ << " entering"; + CHECK(!init_done_); + + Runtime* const runtime = Runtime::Current(); + Thread* const self = Thread::Current(); + gc::Heap* const heap = runtime->GetHeap(); + std::vector spaces = heap->GetBootImageSpaces(); + CHECK(!spaces.empty()); + const ImageHeader& image_header = spaces[0]->GetImageHeader(); + uint32_t pointer_size_unchecked = image_header.GetPointerSizeUnchecked(); + if (!ValidPointerSize(pointer_size_unchecked)) { + *error_msg = StringPrintf("Invalid image pointer size: %u", pointer_size_unchecked); + return false; + } + image_pointer_size_ = image_header.GetPointerSize(); + if (!runtime->IsAotCompiler()) { + // Only the Aot compiler supports having an image with a different pointer size than the + // runtime. This happens on the host for compiling 32 bit tests since we use a 64 bit libart + // compiler. We may also use 32 bit dex2oat on a system with 64 bit apps. + if (image_pointer_size_ != kRuntimePointerSize) { + *error_msg = StringPrintf("Runtime must use current image pointer size: %zu vs %zu", + static_cast(image_pointer_size_), + sizeof(void*)); + return false; + } + } + DCHECK(!runtime->HasResolutionMethod()); + runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod)); + runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod)); + runtime->SetImtUnimplementedMethod( + image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod)); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveAllCalleeSavesMethod), + CalleeSaveType::kSaveAllCalleeSaves); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveRefsOnlyMethod), + CalleeSaveType::kSaveRefsOnly); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod), + CalleeSaveType::kSaveRefsAndArgs); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveEverythingMethod), + CalleeSaveType::kSaveEverything); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveEverythingMethodForClinit), + CalleeSaveType::kSaveEverythingForClinit); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveEverythingMethodForSuspendCheck), + CalleeSaveType::kSaveEverythingForSuspendCheck); + + std::vector oat_files = + runtime->GetOatFileManager().RegisterImageOatFiles(spaces); + DCHECK(!oat_files.empty()); + const OatHeader& default_oat_header = oat_files[0]->GetOatHeader(); + jni_dlsym_lookup_trampoline_ = default_oat_header.GetJniDlsymLookupTrampoline(); + jni_dlsym_lookup_critical_trampoline_ = default_oat_header.GetJniDlsymLookupCriticalTrampoline(); + quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline(); + quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline(); + quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline(); + quick_to_interpreter_bridge_trampoline_ = default_oat_header.GetQuickToInterpreterBridge(); + nterp_trampoline_ = default_oat_header.GetNterpTrampoline(); + if (kIsDebugBuild) { + // Check that the other images use the same trampoline. + for (size_t i = 1; i < oat_files.size(); ++i) { + const OatHeader& ith_oat_header = oat_files[i]->GetOatHeader(); + const void* ith_jni_dlsym_lookup_trampoline_ = + ith_oat_header.GetJniDlsymLookupTrampoline(); + const void* ith_jni_dlsym_lookup_critical_trampoline_ = + ith_oat_header.GetJniDlsymLookupCriticalTrampoline(); + const void* ith_quick_resolution_trampoline = + ith_oat_header.GetQuickResolutionTrampoline(); + const void* ith_quick_imt_conflict_trampoline = + ith_oat_header.GetQuickImtConflictTrampoline(); + const void* ith_quick_generic_jni_trampoline = + ith_oat_header.GetQuickGenericJniTrampoline(); + const void* ith_quick_to_interpreter_bridge_trampoline = + ith_oat_header.GetQuickToInterpreterBridge(); + const void* ith_nterp_trampoline = + ith_oat_header.GetNterpTrampoline(); + if (ith_jni_dlsym_lookup_trampoline_ != jni_dlsym_lookup_trampoline_ || + ith_jni_dlsym_lookup_critical_trampoline_ != jni_dlsym_lookup_critical_trampoline_ || + ith_quick_resolution_trampoline != quick_resolution_trampoline_ || + ith_quick_imt_conflict_trampoline != quick_imt_conflict_trampoline_ || + ith_quick_generic_jni_trampoline != quick_generic_jni_trampoline_ || + ith_quick_to_interpreter_bridge_trampoline != quick_to_interpreter_bridge_trampoline_ || + ith_nterp_trampoline != nterp_trampoline_) { + // Make sure that all methods in this image do not contain those trampolines as + // entrypoints. Otherwise the class-linker won't be able to work with a single set. + TrampolineCheckData data; + data.error = false; + data.pointer_size = GetImagePointerSize(); + data.quick_resolution_trampoline = ith_quick_resolution_trampoline; + data.quick_imt_conflict_trampoline = ith_quick_imt_conflict_trampoline; + data.quick_generic_jni_trampoline = ith_quick_generic_jni_trampoline; + data.quick_to_interpreter_bridge_trampoline = ith_quick_to_interpreter_bridge_trampoline; + data.nterp_trampoline = ith_nterp_trampoline; + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); + auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { + if (obj->IsClass()) { + ObjPtr klass = obj->AsClass(); + for (ArtMethod& m : klass->GetMethods(data.pointer_size)) { + const void* entrypoint = + m.GetEntryPointFromQuickCompiledCodePtrSize(data.pointer_size); + if (entrypoint == data.quick_resolution_trampoline || + entrypoint == data.quick_imt_conflict_trampoline || + entrypoint == data.quick_generic_jni_trampoline || + entrypoint == data.quick_to_interpreter_bridge_trampoline) { + data.m = &m; + data.error = true; + return; + } + } + } + }; + spaces[i]->GetLiveBitmap()->Walk(visitor); + if (data.error) { + ArtMethod* m = data.m; + LOG(ERROR) << "Found a broken ArtMethod: " << ArtMethod::PrettyMethod(m); + *error_msg = "Found an ArtMethod with a bad entrypoint"; + return false; + } + } + } + } + + class_roots_ = GcRoot>( + ObjPtr>::DownCast( + image_header.GetImageRoot(ImageHeader::kClassRoots))); + DCHECK_EQ(GetClassRoot(this)->GetClassFlags(), mirror::kClassFlagClass); + + DCHECK_EQ(GetClassRoot(this)->GetObjectSize(), sizeof(mirror::Object)); + ObjPtr> boot_image_live_objects = + ObjPtr>::DownCast( + image_header.GetImageRoot(ImageHeader::kBootImageLiveObjects)); + runtime->SetSentinel(boot_image_live_objects->Get(ImageHeader::kClearedJniWeakSentinel)); + DCHECK(runtime->GetSentinel().Read()->GetClass() == GetClassRoot(this)); + + for (size_t i = 0u, size = spaces.size(); i != size; ++i) { + // Boot class loader, use a null handle. + std::vector> dex_files; + if (!AddImageSpace(spaces[i], + ScopedNullHandle(), + /*out*/&dex_files, + error_msg)) { + return false; + } + // Append opened dex files at the end. + boot_dex_files_.insert(boot_dex_files_.end(), + std::make_move_iterator(dex_files.begin()), + std::make_move_iterator(dex_files.end())); + } + for (const std::unique_ptr& dex_file : boot_dex_files_) { + OatDexFile::MadviseDexFile(*dex_file, MadviseState::kMadviseStateAtLoad); + } + InitializeObjectVirtualMethodHashes(GetClassRoot(this), + image_pointer_size_, + ArrayRef(object_virtual_method_hashes_)); + FinishInit(self); + + VLOG(startup) << __FUNCTION__ << " exiting"; + return true; +} + +void ClassLinker::AddExtraBootDexFiles( + Thread* self, + std::vector>&& additional_dex_files) { + for (std::unique_ptr& dex_file : additional_dex_files) { + AppendToBootClassPath(self, dex_file.get()); + if (kIsDebugBuild) { + for (const auto& boot_dex_file : boot_dex_files_) { + DCHECK_NE(boot_dex_file->GetLocation(), dex_file->GetLocation()); + } + } + boot_dex_files_.push_back(std::move(dex_file)); + } +} + +bool ClassLinker::IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + ObjPtr class_loader) { + return class_loader == nullptr || + soa.Decode(WellKnownClasses::java_lang_BootClassLoader) == + class_loader->GetClass(); +} + +class CHAOnDeleteUpdateClassVisitor { + public: + explicit CHAOnDeleteUpdateClassVisitor(LinearAlloc* alloc) + : allocator_(alloc), cha_(Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()), + pointer_size_(Runtime::Current()->GetClassLinker()->GetImagePointerSize()), + self_(Thread::Current()) {} + + bool operator()(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) { + // This class is going to be unloaded. Tell CHA about it. + cha_->ResetSingleImplementationInHierarchy(klass, allocator_, pointer_size_); + return true; + } + private: + const LinearAlloc* allocator_; + const ClassHierarchyAnalysis* cha_; + const PointerSize pointer_size_; + const Thread* self_; +}; + +/* + * A class used to ensure that all references to strings interned in an AppImage have been + * properly recorded in the interned references list, and is only ever run in debug mode. + */ +class CountInternedStringReferencesVisitor { + public: + CountInternedStringReferencesVisitor(const gc::space::ImageSpace& space, + const InternTable::UnorderedSet& image_interns) + : space_(space), + image_interns_(image_interns), + count_(0u) {} + + void TestObject(ObjPtr referred_obj) const + REQUIRES_SHARED(Locks::mutator_lock_) { + if (referred_obj != nullptr && + space_.HasAddress(referred_obj.Ptr()) && + referred_obj->IsString()) { + ObjPtr referred_str = referred_obj->AsString(); + auto it = image_interns_.find(GcRoot(referred_str)); + if (it != image_interns_.end() && it->Read() == referred_str) { + ++count_; + } + } + } + + void VisitRootIfNonNull( + mirror::CompressedReference* root) const + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!root->IsNull()) { + VisitRoot(root); + } + } + + void VisitRoot(mirror::CompressedReference* root) const + REQUIRES_SHARED(Locks::mutator_lock_) { + TestObject(root->AsMirrorPtr()); + } + + // Visit Class Fields + void operator()(ObjPtr obj, + MemberOffset offset, + bool is_static ATTRIBUTE_UNUSED) const + REQUIRES_SHARED(Locks::mutator_lock_) { + // References within image or across images don't need a read barrier. + ObjPtr referred_obj = + obj->GetFieldObject(offset); + TestObject(referred_obj); + } + + void operator()(ObjPtr klass ATTRIBUTE_UNUSED, + ObjPtr ref) const + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { + operator()(ref, mirror::Reference::ReferentOffset(), /*is_static=*/ false); + } + + size_t GetCount() const { + return count_; + } + + private: + const gc::space::ImageSpace& space_; + const InternTable::UnorderedSet& image_interns_; + mutable size_t count_; // Modified from the `const` callbacks. +}; + +/* + * This function counts references to strings interned in the AppImage. + * This is used in debug build to check against the number of the recorded references. + */ +size_t CountInternedStringReferences(gc::space::ImageSpace& space, + const InternTable::UnorderedSet& image_interns) + REQUIRES_SHARED(Locks::mutator_lock_) { + const gc::accounting::ContinuousSpaceBitmap* bitmap = space.GetMarkBitmap(); + const ImageHeader& image_header = space.GetImageHeader(); + const uint8_t* target_base = space.GetMemMap()->Begin(); + const ImageSection& objects_section = image_header.GetObjectsSection(); + + auto objects_begin = reinterpret_cast(target_base + objects_section.Offset()); + auto objects_end = reinterpret_cast(target_base + objects_section.End()); + + CountInternedStringReferencesVisitor visitor(space, image_interns); + bitmap->VisitMarkedRange(objects_begin, + objects_end, + [&space, &visitor](mirror::Object* obj) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (space.HasAddress(obj)) { + if (obj->IsDexCache()) { + obj->VisitReferences(visitor, visitor); + } else { + // Don't visit native roots for non-dex-cache as they can't contain + // native references to strings. This is verified during compilation + // by ImageWriter::VerifyNativeGCRootInvariants. + obj->VisitReferences(visitor, visitor); + } + } + }); + return visitor.GetCount(); +} + +template +static void VisitInternedStringReferences( + gc::space::ImageSpace* space, + const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) { + const uint8_t* target_base = space->Begin(); + const ImageSection& sro_section = + space->GetImageHeader().GetImageStringReferenceOffsetsSection(); + const size_t num_string_offsets = sro_section.Size() / sizeof(AppImageReferenceOffsetInfo); + + VLOG(image) + << "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = " + << num_string_offsets; + + const auto* sro_base = + reinterpret_cast(target_base + sro_section.Offset()); + + for (size_t offset_index = 0; offset_index < num_string_offsets; ++offset_index) { + uint32_t base_offset = sro_base[offset_index].first; + + uint32_t raw_member_offset = sro_base[offset_index].second; + DCHECK_ALIGNED(base_offset, 2); + DCHECK_ALIGNED(raw_member_offset, 2); + + ObjPtr obj_ptr = + reinterpret_cast(space->Begin() + base_offset); + MemberOffset member_offset(raw_member_offset); + ObjPtr referred_string = + obj_ptr->GetFieldObject(member_offset); + DCHECK(referred_string != nullptr); + + ObjPtr visited = visitor(referred_string); + if (visited != referred_string) { + obj_ptr->SetFieldObject(member_offset, visited); + } + } +} + +static void VerifyInternedStringReferences(gc::space::ImageSpace* space) + REQUIRES_SHARED(Locks::mutator_lock_) { + InternTable::UnorderedSet image_interns; + const ImageSection& section = space->GetImageHeader().GetInternedStringsSection(); + if (section.Size() > 0) { + size_t read_count; + const uint8_t* data = space->Begin() + section.Offset(); + InternTable::UnorderedSet image_set(data, /*make_copy_of_data=*/ false, &read_count); + image_set.swap(image_interns); + } + size_t num_recorded_refs = 0u; + VisitInternedStringReferences( + space, + [&image_interns, &num_recorded_refs](ObjPtr str) + REQUIRES_SHARED(Locks::mutator_lock_) { + auto it = image_interns.find(GcRoot(str)); + CHECK(it != image_interns.end()); + CHECK(it->Read() == str); + ++num_recorded_refs; + return str; + }); + size_t num_found_refs = CountInternedStringReferences(*space, image_interns); + CHECK_EQ(num_recorded_refs, num_found_refs); +} + +// new_class_set is the set of classes that were read from the class table section in the image. +// If there was no class table section, it is null. +// Note: using a class here to avoid having to make ClassLinker internals public. +class AppImageLoadingHelper { + public: + static void Update( + ClassLinker* class_linker, + gc::space::ImageSpace* space, + Handle class_loader, + Handle> dex_caches) + REQUIRES(!Locks::dex_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + + static void HandleAppImageStrings(gc::space::ImageSpace* space) + REQUIRES_SHARED(Locks::mutator_lock_); +}; + +void AppImageLoadingHelper::Update( + ClassLinker* class_linker, + gc::space::ImageSpace* space, + Handle class_loader, + Handle> dex_caches) + REQUIRES(!Locks::dex_lock_) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedTrace app_image_timing("AppImage:Updating"); + + if (kIsDebugBuild && ClassLinker::kAppImageMayContainStrings) { + // In debug build, verify the string references before applying + // the Runtime::LoadAppImageStartupCache() option. + VerifyInternedStringReferences(space); + } + + Thread* const self = Thread::Current(); + Runtime* const runtime = Runtime::Current(); + gc::Heap* const heap = runtime->GetHeap(); + const ImageHeader& header = space->GetImageHeader(); + { + // Register dex caches with the class loader. + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + for (auto dex_cache : dex_caches.Iterate()) { + const DexFile* const dex_file = dex_cache->GetDexFile(); + { + WriterMutexLock mu2(self, *Locks::dex_lock_); + CHECK(class_linker->FindDexCacheDataLocked(*dex_file) == nullptr); + class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get()); + } + } + } + + if (ClassLinker::kAppImageMayContainStrings) { + HandleAppImageStrings(space); + } + + if (kVerifyArtMethodDeclaringClasses) { + ScopedTrace timing("AppImage:VerifyDeclaringClasses"); + ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_); + gc::accounting::HeapBitmap* live_bitmap = heap->GetLiveBitmap(); + header.VisitPackedArtMethods([&](ArtMethod& method) + REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + ObjPtr klass = method.GetDeclaringClassUnchecked(); + if (klass != nullptr) { + CHECK(live_bitmap->Test(klass.Ptr())) << "Image method has unmarked declaring class"; + } + }, space->Begin(), kRuntimePointerSize); + } +} + +void AppImageLoadingHelper::HandleAppImageStrings(gc::space::ImageSpace* space) { + // Iterate over the string reference offsets stored in the image and intern + // the strings they point to. + ScopedTrace timing("AppImage:InternString"); + + Runtime* const runtime = Runtime::Current(); + InternTable* const intern_table = runtime->GetInternTable(); + + // Add the intern table, removing any conflicts. For conflicts, store the new address in a map + // for faster lookup. + // TODO: Optimize with a bitmap or bloom filter + SafeMap intern_remap; + auto func = [&](InternTable::UnorderedSet& interns) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(Locks::intern_table_lock_) { + const size_t non_boot_image_strings = intern_table->CountInterns( + /*visit_boot_images=*/false, + /*visit_non_boot_images=*/true); + VLOG(image) << "AppImage:stringsInInternTableSize = " << interns.size(); + VLOG(image) << "AppImage:nonBootImageInternStrings = " << non_boot_image_strings; + // Visit the smaller of the two sets to compute the intersection. + if (interns.size() < non_boot_image_strings) { + for (auto it = interns.begin(); it != interns.end(); ) { + ObjPtr string = it->Read(); + ObjPtr existing = intern_table->LookupWeakLocked(string); + if (existing == nullptr) { + existing = intern_table->LookupStrongLocked(string); + } + if (existing != nullptr) { + intern_remap.Put(string.Ptr(), existing.Ptr()); + it = interns.erase(it); + } else { + ++it; + } + } + } else { + intern_table->VisitInterns([&](const GcRoot& root) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(Locks::intern_table_lock_) { + auto it = interns.find(root); + if (it != interns.end()) { + ObjPtr existing = root.Read(); + intern_remap.Put(it->Read(), existing.Ptr()); + it = interns.erase(it); + } + }, /*visit_boot_images=*/false, /*visit_non_boot_images=*/true); + } + // Consistency check to ensure correctness. + if (kIsDebugBuild) { + for (GcRoot& root : interns) { + ObjPtr string = root.Read(); + CHECK(intern_table->LookupWeakLocked(string) == nullptr) << string->ToModifiedUtf8(); + CHECK(intern_table->LookupStrongLocked(string) == nullptr) << string->ToModifiedUtf8(); + } + } + }; + intern_table->AddImageStringsToTable(space, func); + if (!intern_remap.empty()) { + VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size(); + VisitInternedStringReferences( + space, + [&intern_remap](ObjPtr str) REQUIRES_SHARED(Locks::mutator_lock_) { + auto it = intern_remap.find(str.Ptr()); + if (it != intern_remap.end()) { + return ObjPtr(it->second); + } + return str; + }); + } +} + +static std::unique_ptr OpenOatDexFile(const OatFile* oat_file, + const char* location, + std::string* error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(error_msg != nullptr); + std::unique_ptr dex_file; + const OatDexFile* oat_dex_file = oat_file->GetOatDexFile(location, nullptr, error_msg); + if (oat_dex_file == nullptr) { + return std::unique_ptr(); + } + std::string inner_error_msg; + dex_file = oat_dex_file->OpenDexFile(&inner_error_msg); + if (dex_file == nullptr) { + *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'", + location, + oat_file->GetLocation().c_str(), + inner_error_msg.c_str()); + return std::unique_ptr(); + } + + if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) { + *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x", + location, + dex_file->GetLocationChecksum(), + oat_dex_file->GetDexFileLocationChecksum()); + return std::unique_ptr(); + } + return dex_file; +} + +bool ClassLinker::OpenImageDexFiles(gc::space::ImageSpace* space, + std::vector>* out_dex_files, + std::string* error_msg) { + ScopedAssertNoThreadSuspension nts(__FUNCTION__); + const ImageHeader& header = space->GetImageHeader(); + ObjPtr dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches); + DCHECK(dex_caches_object != nullptr); + ObjPtr> dex_caches = + dex_caches_object->AsObjectArray(); + const OatFile* oat_file = space->GetOatFile(); + for (auto dex_cache : dex_caches->Iterate()) { + std::string dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); + std::unique_ptr dex_file = OpenOatDexFile(oat_file, + dex_file_location.c_str(), + error_msg); + if (dex_file == nullptr) { + return false; + } + dex_cache->SetDexFile(dex_file.get()); + out_dex_files->push_back(std::move(dex_file)); + } + return true; +} + +// Helper class for ArtMethod checks when adding an image. Keeps all required functionality +// together and caches some intermediate results. +class ImageChecker final { + public: + static void CheckObjects(gc::Heap* heap, ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + ImageChecker ic(heap, class_linker); + auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(obj != nullptr); + CHECK(obj->GetClass() != nullptr) << "Null class in object " << obj; + CHECK(obj->GetClass()->GetClass() != nullptr) << "Null class class " << obj; + if (obj->IsClass()) { + auto klass = obj->AsClass(); + for (ArtField& field : klass->GetIFields()) { + CHECK_EQ(field.GetDeclaringClass(), klass); + } + for (ArtField& field : klass->GetSFields()) { + CHECK_EQ(field.GetDeclaringClass(), klass); + } + const PointerSize pointer_size = ic.pointer_size_; + for (ArtMethod& m : klass->GetMethods(pointer_size)) { + ic.CheckArtMethod(&m, klass); + } + ObjPtr vtable = klass->GetVTable(); + if (vtable != nullptr) { + ic.CheckArtMethodPointerArray(vtable, nullptr); + } + if (klass->ShouldHaveImt()) { + ImTable* imt = klass->GetImt(pointer_size); + for (size_t i = 0; i < ImTable::kSize; ++i) { + ic.CheckArtMethod(imt->Get(i, pointer_size), nullptr); + } + } + if (klass->ShouldHaveEmbeddedVTable()) { + for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) { + ic.CheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr); + } + } + ObjPtr iftable = klass->GetIfTable(); + for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { + if (iftable->GetMethodArrayCount(i) > 0) { + ic.CheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr); + } + } + } + }; + heap->VisitObjects(visitor); + } + + private: + ImageChecker(gc::Heap* heap, ClassLinker* class_linker) + : spaces_(heap->GetBootImageSpaces()), + pointer_size_(class_linker->GetImagePointerSize()) { + space_begin_.reserve(spaces_.size()); + method_sections_.reserve(spaces_.size()); + runtime_method_sections_.reserve(spaces_.size()); + for (gc::space::ImageSpace* space : spaces_) { + space_begin_.push_back(space->Begin()); + auto& header = space->GetImageHeader(); + method_sections_.push_back(&header.GetMethodsSection()); + runtime_method_sections_.push_back(&header.GetRuntimeMethodsSection()); + } + } + + void CheckArtMethod(ArtMethod* m, ObjPtr expected_class) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (m->IsRuntimeMethod()) { + ObjPtr declaring_class = m->GetDeclaringClassUnchecked(); + CHECK(declaring_class == nullptr) << declaring_class << " " << m->PrettyMethod(); + } else if (m->IsCopied()) { + CHECK(m->GetDeclaringClass() != nullptr) << m->PrettyMethod(); + } else if (expected_class != nullptr) { + CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << m->PrettyMethod(); + } + if (!spaces_.empty()) { + bool contains = false; + for (size_t i = 0; !contains && i != space_begin_.size(); ++i) { + const size_t offset = reinterpret_cast(m) - space_begin_[i]; + contains = method_sections_[i]->Contains(offset) || + runtime_method_sections_[i]->Contains(offset); + } + CHECK(contains) << m << " not found"; + } + } + + void CheckArtMethodPointerArray(ObjPtr arr, + ObjPtr expected_class) + REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(arr != nullptr); + for (int32_t j = 0; j < arr->GetLength(); ++j) { + auto* method = arr->GetElementPtrSize(j, pointer_size_); + // expected_class == null means we are a dex cache. + if (expected_class != nullptr) { + CHECK(method != nullptr); + } + if (method != nullptr) { + CheckArtMethod(method, expected_class); + } + } + } + + const std::vector& spaces_; + const PointerSize pointer_size_; + + // Cached sections from the spaces. + std::vector space_begin_; + std::vector method_sections_; + std::vector runtime_method_sections_; +}; + +static void VerifyAppImage(const ImageHeader& header, + const Handle& class_loader, + ClassTable* class_table, + gc::space::ImageSpace* space) + REQUIRES_SHARED(Locks::mutator_lock_) { + header.VisitPackedArtMethods([&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr klass = method.GetDeclaringClass(); + if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { + CHECK_EQ(class_table->LookupByDescriptor(klass), klass) + << mirror::Class::PrettyClass(klass); + } + }, space->Begin(), kRuntimePointerSize); + { + // Verify that all direct interfaces of classes in the class table are also resolved. + std::vector> classes; + auto verify_direct_interfaces_in_table = [&](ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader.Get()) { + classes.push_back(klass); + } + return true; + }; + class_table->Visit(verify_direct_interfaces_in_table); + Thread* self = Thread::Current(); + for (ObjPtr klass : classes) { + for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) { + CHECK(klass->GetDirectInterface(self, klass, i) != nullptr) + << klass->PrettyDescriptor() << " iface #" << i; + } + } + } +} + +bool ClassLinker::AddImageSpace( + gc::space::ImageSpace* space, + Handle class_loader, + std::vector>* out_dex_files, + std::string* error_msg) { + DCHECK(out_dex_files != nullptr); + DCHECK(error_msg != nullptr); + const uint64_t start_time = NanoTime(); + const bool app_image = class_loader != nullptr; + const ImageHeader& header = space->GetImageHeader(); + ObjPtr dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches); + DCHECK(dex_caches_object != nullptr); + Runtime* const runtime = Runtime::Current(); + gc::Heap* const heap = runtime->GetHeap(); + Thread* const self = Thread::Current(); + // Check that the image is what we are expecting. + if (image_pointer_size_ != space->GetImageHeader().GetPointerSize()) { + *error_msg = StringPrintf("Application image pointer size does not match runtime: %zu vs %zu", + static_cast(space->GetImageHeader().GetPointerSize()), + image_pointer_size_); + return false; + } + size_t expected_image_roots = ImageHeader::NumberOfImageRoots(app_image); + if (static_cast(header.GetImageRoots()->GetLength()) != expected_image_roots) { + *error_msg = StringPrintf("Expected %zu image roots but got %d", + expected_image_roots, + header.GetImageRoots()->GetLength()); + return false; + } + StackHandleScope<3> hs(self); + Handle> dex_caches( + hs.NewHandle(dex_caches_object->AsObjectArray())); + Handle> class_roots(hs.NewHandle( + header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray())); + MutableHandle image_class_loader(hs.NewHandle( + app_image ? header.GetImageRoot(ImageHeader::kAppImageClassLoader)->AsClassLoader() + : nullptr)); + DCHECK(class_roots != nullptr); + if (class_roots->GetLength() != static_cast(ClassRoot::kMax)) { + *error_msg = StringPrintf("Expected %d class roots but got %d", + class_roots->GetLength(), + static_cast(ClassRoot::kMax)); + return false; + } + // Check against existing class roots to make sure they match the ones in the boot image. + ObjPtr> existing_class_roots = GetClassRoots(); + for (size_t i = 0; i < static_cast(ClassRoot::kMax); i++) { + if (class_roots->Get(i) != GetClassRoot(static_cast(i), existing_class_roots)) { + *error_msg = "App image class roots must have pointer equality with runtime ones."; + return false; + } + } + const OatFile* oat_file = space->GetOatFile(); + if (oat_file->GetOatHeader().GetDexFileCount() != + static_cast(dex_caches->GetLength())) { + *error_msg = "Dex cache count and dex file count mismatch while trying to initialize from " + "image"; + return false; + } + + for (auto dex_cache : dex_caches.Iterate()) { + std::string dex_file_location = dex_cache->GetLocation()->ToModifiedUtf8(); + std::unique_ptr dex_file = OpenOatDexFile(oat_file, + dex_file_location.c_str(), + error_msg); + if (dex_file == nullptr) { + return false; + } + + LinearAlloc* linear_alloc = GetOrCreateAllocatorForClassLoader(class_loader.Get()); + DCHECK(linear_alloc != nullptr); + DCHECK_EQ(linear_alloc == Runtime::Current()->GetLinearAlloc(), !app_image); + { + // Native fields are all null. Initialize them and allocate native memory. + WriterMutexLock mu(self, *Locks::dex_lock_); + dex_cache->InitializeNativeFields(dex_file.get(), linear_alloc); + } + if (!app_image) { + // Register dex files, keep track of existing ones that are conflicts. + AppendToBootClassPath(dex_file.get(), dex_cache); + } + out_dex_files->push_back(std::move(dex_file)); + } + + if (app_image) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + ScopedAssertNoThreadSuspension sants("Checking app image", soa.Self()); + if (IsBootClassLoader(soa, image_class_loader.Get())) { + *error_msg = "Unexpected BootClassLoader in app image"; + return false; + } + } + + if (kCheckImageObjects) { + if (!app_image) { + ImageChecker::CheckObjects(heap, this); + } + } + + // Set entry point to interpreter if in InterpretOnly mode. + if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) { + // Set image methods' entry point to interpreter. + header.VisitPackedArtMethods([&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) { + if (!method.IsRuntimeMethod()) { + DCHECK(method.GetDeclaringClass() != nullptr); + if (!method.IsNative() && !method.IsResolutionMethod()) { + method.SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), + image_pointer_size_); + } + } + }, space->Begin(), image_pointer_size_); + } + + if (!runtime->IsAotCompiler()) { + ScopedTrace trace("AppImage:UpdateCodeItemAndNterp"); + bool can_use_nterp = interpreter::CanRuntimeUseNterp(); + header.VisitPackedArtMethods([&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) { + // In the image, the `data` pointer field of the ArtMethod contains the code + // item offset. Change this to the actual pointer to the code item. + if (method.HasCodeItem()) { + const dex::CodeItem* code_item = method.GetDexFile()->GetCodeItem( + reinterpret_cast32(method.GetDataPtrSize(image_pointer_size_))); + method.SetCodeItem(code_item); + } + // Set image methods' entry point that point to the interpreter bridge to the + // nterp entry point. + if (method.GetEntryPointFromQuickCompiledCode() == nterp_trampoline_) { + if (can_use_nterp) { + DCHECK(!NeedsClinitCheckBeforeCall(&method) || + method.GetDeclaringClass()->IsVisiblyInitialized()); + method.SetEntryPointFromQuickCompiledCode(interpreter::GetNterpEntryPoint()); + } else { + method.SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } + } + }, space->Begin(), image_pointer_size_); + } + + if (runtime->IsVerificationSoftFail()) { + header.VisitPackedArtMethods([&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) { + if (!method.IsNative() && method.IsInvokable()) { + method.ClearSkipAccessChecks(); + } + }, space->Begin(), image_pointer_size_); + } + + ClassTable* class_table = nullptr; + { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + class_table = InsertClassTableForClassLoader(class_loader.Get()); + } + // If we have a class table section, read it and use it for verification in + // UpdateAppImageClassLoadersAndDexCaches. + ClassTable::ClassSet temp_set; + const ImageSection& class_table_section = header.GetClassTableSection(); + const bool added_class_table = class_table_section.Size() > 0u; + if (added_class_table) { + const uint64_t start_time2 = NanoTime(); + size_t read_count = 0; + temp_set = ClassTable::ClassSet(space->Begin() + class_table_section.Offset(), + /*make copy*/false, + &read_count); + VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2); + } + if (app_image) { + AppImageLoadingHelper::Update(this, space, class_loader, dex_caches); + + { + ScopedTrace trace("AppImage:UpdateClassLoaders"); + // Update class loader and resolved strings. If added_class_table is false, the resolved + // strings were forwarded UpdateAppImageClassLoadersAndDexCaches. + ObjPtr loader(class_loader.Get()); + for (const ClassTable::TableSlot& root : temp_set) { + // Note: We probably don't need the read barrier unless we copy the app image objects into + // the region space. + ObjPtr klass(root.Read()); + // Do not update class loader for boot image classes where the app image + // class loader is only the initiating loader but not the defining loader. + // Avoid read barrier since we are comparing against null. + if (klass->GetClassLoader() != nullptr) { + klass->SetClassLoader(loader); + } + } + } + + if (kBitstringSubtypeCheckEnabled) { + // Every class in the app image has initially SubtypeCheckInfo in the + // Uninitialized state. + // + // The SubtypeCheck invariants imply that a SubtypeCheckInfo is at least Initialized + // after class initialization is complete. The app image ClassStatus as-is + // are almost all ClassStatus::Initialized, and being in the + // SubtypeCheckInfo::kUninitialized state is violating that invariant. + // + // Force every app image class's SubtypeCheck to be at least kIninitialized. + // + // See also ImageWriter::FixupClass. + ScopedTrace trace("AppImage:RecacluateSubtypeCheckBitstrings"); + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + for (const ClassTable::TableSlot& root : temp_set) { + SubtypeCheck>::EnsureInitialized(root.Read()); + } + } + } + if (!oat_file->GetBssGcRoots().empty()) { + // Insert oat file to class table for visiting .bss GC roots. + class_table->InsertOatFile(oat_file); + } + + if (added_class_table) { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + class_table->AddClassSet(std::move(temp_set)); + } + + if (kIsDebugBuild && app_image) { + // This verification needs to happen after the classes have been added to the class loader. + // Since it ensures classes are in the class table. + ScopedTrace trace("AppImage:Verify"); + VerifyAppImage(header, class_loader, class_table, space); + } + + VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time); + return true; +} + +void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { + // Acquire tracing_enabled before locking class linker lock to prevent lock order violation. Since + // enabling tracing requires the mutator lock, there are no race conditions here. + const bool tracing_enabled = Trace::IsTracingEnabled(); + Thread* const self = Thread::Current(); + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + if (kUseReadBarrier) { + // We do not track new roots for CC. + DCHECK_EQ(0, flags & (kVisitRootFlagNewRoots | + kVisitRootFlagClearRootLog | + kVisitRootFlagStartLoggingNewRoots | + kVisitRootFlagStopLoggingNewRoots)); + } + if ((flags & kVisitRootFlagAllRoots) != 0) { + // Argument for how root visiting deals with ArtField and ArtMethod roots. + // There is 3 GC cases to handle: + // Non moving concurrent: + // This case is easy to handle since the reference members of ArtMethod and ArtFields are held + // live by the class and class roots. + // + // Moving non-concurrent: + // This case needs to call visit VisitNativeRoots in case the classes or dex cache arrays move. + // To prevent missing roots, this case needs to ensure that there is no + // suspend points between the point which we allocate ArtMethod arrays and place them in a + // class which is in the class table. + // + // Moving concurrent: + // Need to make sure to not copy ArtMethods without doing read barriers since the roots are + // marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy. + // + // Use an unbuffered visitor since the class table uses a temporary GcRoot for holding decoded + // ClassTable::TableSlot. The buffered root visiting would access a stale stack location for + // these objects. + UnbufferedRootVisitor root_visitor(visitor, RootInfo(kRootStickyClass)); + boot_class_table_->VisitRoots(root_visitor); + // If tracing is enabled, then mark all the class loaders to prevent unloading. + if ((flags & kVisitRootFlagClassLoader) != 0 || tracing_enabled) { + for (const ClassLoaderData& data : class_loaders_) { + GcRoot root(GcRoot(self->DecodeJObject(data.weak_root))); + root.VisitRoot(visitor, RootInfo(kRootVMInternal)); + } + } + } else if (!kUseReadBarrier && (flags & kVisitRootFlagNewRoots) != 0) { + for (auto& root : new_class_roots_) { + ObjPtr old_ref = root.Read(); + root.VisitRoot(visitor, RootInfo(kRootStickyClass)); + ObjPtr new_ref = root.Read(); + // Concurrent moving GC marked new roots through the to-space invariant. + CHECK_EQ(new_ref, old_ref); + } + for (const OatFile* oat_file : new_bss_roots_boot_oat_files_) { + for (GcRoot& root : oat_file->GetBssGcRoots()) { + ObjPtr old_ref = root.Read(); + if (old_ref != nullptr) { + DCHECK(old_ref->IsClass()); + root.VisitRoot(visitor, RootInfo(kRootStickyClass)); + ObjPtr new_ref = root.Read(); + // Concurrent moving GC marked new roots through the to-space invariant. + CHECK_EQ(new_ref, old_ref); + } + } + } + } + if (!kUseReadBarrier && (flags & kVisitRootFlagClearRootLog) != 0) { + new_class_roots_.clear(); + new_bss_roots_boot_oat_files_.clear(); + } + if (!kUseReadBarrier && (flags & kVisitRootFlagStartLoggingNewRoots) != 0) { + log_new_roots_ = true; + } else if (!kUseReadBarrier && (flags & kVisitRootFlagStopLoggingNewRoots) != 0) { + log_new_roots_ = false; + } + // We deliberately ignore the class roots in the image since we + // handle image roots by using the MS/CMS rescanning of dirty cards. +} + +// Keep in sync with InitCallback. Anything we visit, we need to +// reinit references to when reinitializing a ClassLinker from a +// mapped image. +void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { + class_roots_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal)); + VisitClassRoots(visitor, flags); + // Instead of visiting the find_array_class_cache_ drop it so that it doesn't prevent class + // unloading if we are marking roots. + DropFindArrayClassCache(); +} + +class VisitClassLoaderClassesVisitor : public ClassLoaderVisitor { + public: + explicit VisitClassLoaderClassesVisitor(ClassVisitor* visitor) + : visitor_(visitor), + done_(false) {} + + void Visit(ObjPtr class_loader) + REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) override { + ClassTable* const class_table = class_loader->GetClassTable(); + if (!done_ && class_table != nullptr) { + DefiningClassLoaderFilterVisitor visitor(class_loader, visitor_); + if (!class_table->Visit(visitor)) { + // If the visitor ClassTable returns false it means that we don't need to continue. + done_ = true; + } + } + } + + private: + // Class visitor that limits the class visits from a ClassTable to the classes with + // the provided defining class loader. This filter is used to avoid multiple visits + // of the same class which can be recorded for multiple initiating class loaders. + class DefiningClassLoaderFilterVisitor : public ClassVisitor { + public: + DefiningClassLoaderFilterVisitor(ObjPtr defining_class_loader, + ClassVisitor* visitor) + : defining_class_loader_(defining_class_loader), visitor_(visitor) { } + + bool operator()(ObjPtr klass) override REQUIRES_SHARED(Locks::mutator_lock_) { + if (klass->GetClassLoader() != defining_class_loader_) { + return true; + } + return (*visitor_)(klass); + } + + const ObjPtr defining_class_loader_; + ClassVisitor* const visitor_; + }; + + ClassVisitor* const visitor_; + // If done is true then we don't need to do any more visiting. + bool done_; +}; + +void ClassLinker::VisitClassesInternal(ClassVisitor* visitor) { + if (boot_class_table_->Visit(*visitor)) { + VisitClassLoaderClassesVisitor loader_visitor(visitor); + VisitClassLoaders(&loader_visitor); + } +} + +void ClassLinker::VisitClasses(ClassVisitor* visitor) { + Thread* const self = Thread::Current(); + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + // Not safe to have thread suspension when we are holding a lock. + if (self != nullptr) { + ScopedAssertNoThreadSuspension nts(__FUNCTION__); + VisitClassesInternal(visitor); + } else { + VisitClassesInternal(visitor); + } +} + +class GetClassesInToVector : public ClassVisitor { + public: + bool operator()(ObjPtr klass) override { + classes_.push_back(klass); + return true; + } + std::vector> classes_; +}; + +class GetClassInToObjectArray : public ClassVisitor { + public: + explicit GetClassInToObjectArray(mirror::ObjectArray* arr) + : arr_(arr), index_(0) {} + + bool operator()(ObjPtr klass) override REQUIRES_SHARED(Locks::mutator_lock_) { + ++index_; + if (index_ <= arr_->GetLength()) { + arr_->Set(index_ - 1, klass); + return true; + } + return false; + } + + bool Succeeded() const REQUIRES_SHARED(Locks::mutator_lock_) { + return index_ <= arr_->GetLength(); + } + + private: + mirror::ObjectArray* const arr_; + int32_t index_; +}; + +void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { + // TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem + // is avoiding duplicates. + if (!kMovingClasses) { + ScopedAssertNoThreadSuspension nts(__FUNCTION__); + GetClassesInToVector accumulator; + VisitClasses(&accumulator); + for (ObjPtr klass : accumulator.classes_) { + if (!visitor->operator()(klass)) { + return; + } + } + } else { + Thread* const self = Thread::Current(); + StackHandleScope<1> hs(self); + auto classes = hs.NewHandle>(nullptr); + // We size the array assuming classes won't be added to the class table during the visit. + // If this assumption fails we iterate again. + while (true) { + size_t class_table_size; + { + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + // Add 100 in case new classes get loaded when we are filling in the object array. + class_table_size = NumZygoteClasses() + NumNonZygoteClasses() + 100; + } + ObjPtr array_of_class = GetClassRoot>(this); + classes.Assign( + mirror::ObjectArray::Alloc(self, array_of_class, class_table_size)); + CHECK(classes != nullptr); // OOME. + GetClassInToObjectArray accumulator(classes.Get()); + VisitClasses(&accumulator); + if (accumulator.Succeeded()) { + break; + } + } + for (int32_t i = 0; i < classes->GetLength(); ++i) { + // If the class table shrank during creation of the clases array we expect null elements. If + // the class table grew then the loop repeats. If classes are created after the loop has + // finished then we don't visit. + ObjPtr klass = classes->Get(i); + if (klass != nullptr && !visitor->operator()(klass)) { + return; + } + } + } +} + +ClassLinker::~ClassLinker() { + Thread* const self = Thread::Current(); + for (const ClassLoaderData& data : class_loaders_) { + // CHA unloading analysis is not needed. No negative consequences are expected because + // all the classloaders are deleted at the same time. + DeleteClassLoader(self, data, /*cleanup_cha=*/ false); + } + class_loaders_.clear(); + while (!running_visibly_initialized_callbacks_.empty()) { + std::unique_ptr callback( + std::addressof(running_visibly_initialized_callbacks_.front())); + running_visibly_initialized_callbacks_.pop_front(); + } +} + +void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data, bool cleanup_cha) { + Runtime* const runtime = Runtime::Current(); + JavaVMExt* const vm = runtime->GetJavaVM(); + vm->DeleteWeakGlobalRef(self, data.weak_root); + // Notify the JIT that we need to remove the methods and/or profiling info. + if (runtime->GetJit() != nullptr) { + jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache(); + if (code_cache != nullptr) { + // For the JIT case, RemoveMethodsIn removes the CHA dependencies. + code_cache->RemoveMethodsIn(self, *data.allocator); + } + } else if (cha_ != nullptr) { + // If we don't have a JIT, we need to manually remove the CHA dependencies manually. + cha_->RemoveDependenciesForLinearAlloc(data.allocator); + } + // Cleanup references to single implementation ArtMethods that will be deleted. + if (cleanup_cha) { + CHAOnDeleteUpdateClassVisitor visitor(data.allocator); + data.class_table->Visit(visitor); + } + { + MutexLock lock(self, critical_native_code_with_clinit_check_lock_); + auto end = critical_native_code_with_clinit_check_.end(); + for (auto it = critical_native_code_with_clinit_check_.begin(); it != end; ) { + if (data.allocator->ContainsUnsafe(it->first)) { + it = critical_native_code_with_clinit_check_.erase(it); + } else { + ++it; + } + } + } + + delete data.allocator; + delete data.class_table; +} + +ObjPtr ClassLinker::AllocPointerArray(Thread* self, size_t length) { + return ObjPtr::DownCast( + image_pointer_size_ == PointerSize::k64 + ? ObjPtr(mirror::LongArray::Alloc(self, length)) + : ObjPtr(mirror::IntArray::Alloc(self, length))); +} + +ObjPtr ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) { + StackHandleScope<1> hs(self); + auto dex_cache(hs.NewHandle(ObjPtr::DownCast( + GetClassRoot(this)->AllocObject(self)))); + if (dex_cache == nullptr) { + self->AssertPendingOOMException(); + return nullptr; + } + // Use InternWeak() so that the location String can be collected when the ClassLoader + // with this DexCache is collected. + ObjPtr location = intern_table_->InternWeak(dex_file.GetLocation().c_str()); + if (location == nullptr) { + self->AssertPendingOOMException(); + return nullptr; + } + dex_cache->SetLocation(location); + return dex_cache.Get(); +} + +ObjPtr ClassLinker::AllocAndInitializeDexCache(Thread* self, + const DexFile& dex_file, + LinearAlloc* linear_alloc) { + ObjPtr dex_cache = AllocDexCache(self, dex_file); + if (dex_cache != nullptr) { + WriterMutexLock mu(self, *Locks::dex_lock_); + dex_cache->InitializeNativeFields(&dex_file, linear_alloc); + } + return dex_cache; +} + +template +ObjPtr ClassLinker::AllocClass(Thread* self, + ObjPtr java_lang_Class, + uint32_t class_size, + const PreFenceVisitor& pre_fence_visitor) { + DCHECK_GE(class_size, sizeof(mirror::Class)); + gc::Heap* heap = Runtime::Current()->GetHeap(); + ObjPtr k = (kMovingClasses && kMovable) ? + heap->AllocObject(self, java_lang_Class, class_size, pre_fence_visitor) : + heap->AllocNonMovableObject(self, java_lang_Class, class_size, pre_fence_visitor); + if (UNLIKELY(k == nullptr)) { + self->AssertPendingOOMException(); + return nullptr; + } + return k->AsClass(); +} + +template +ObjPtr ClassLinker::AllocClass(Thread* self, + ObjPtr java_lang_Class, + uint32_t class_size) { + mirror::Class::InitializeClassVisitor visitor(class_size); + return AllocClass(self, java_lang_Class, class_size, visitor); +} + +ObjPtr ClassLinker::AllocClass(Thread* self, uint32_t class_size) { + return AllocClass(self, GetClassRoot(this), class_size); +} + +void ClassLinker::AllocPrimitiveArrayClass(Thread* self, + ClassRoot primitive_root, + ClassRoot array_root) { + // We make this class non-movable for the unlikely case where it were to be + // moved by a sticky-bit (minor) collection when using the Generational + // Concurrent Copying (CC) collector, potentially creating a stale reference + // in the `klass_` field of one of its instances allocated in the Large-Object + // Space (LOS) -- see the comment about the dirty card scanning logic in + // art::gc::collector::ConcurrentCopying::MarkingPhase. + ObjPtr array_class = AllocClass( + self, GetClassRoot(this), mirror::Array::ClassSize(image_pointer_size_)); + ObjPtr component_type = GetClassRoot(primitive_root, this); + DCHECK(component_type->IsPrimitive()); + array_class->SetComponentType(component_type); + SetClassRoot(array_root, array_class); +} + +void ClassLinker::FinishArrayClassSetup(ObjPtr array_class) { + ObjPtr java_lang_Object = GetClassRoot(this); + array_class->SetSuperClass(java_lang_Object); + array_class->SetVTable(java_lang_Object->GetVTable()); + array_class->SetPrimitiveType(Primitive::kPrimNot); + ObjPtr component_type = array_class->GetComponentType(); + array_class->SetClassFlags(component_type->IsPrimitive() + ? mirror::kClassFlagNoReferenceFields + : mirror::kClassFlagObjectArray); + array_class->SetClassLoader(component_type->GetClassLoader()); + array_class->SetStatusForPrimitiveOrArray(ClassStatus::kLoaded); + array_class->PopulateEmbeddedVTable(image_pointer_size_); + ImTable* object_imt = java_lang_Object->GetImt(image_pointer_size_); + array_class->SetImt(object_imt, image_pointer_size_); + // Skip EnsureSkipAccessChecksMethods(). We can skip the verified status, + // the kAccVerificationAttempted flag is added below, and there are no + // methods that need the kAccSkipAccessChecks flag. + DCHECK_EQ(array_class->NumMethods(), 0u); + + // don't need to set new_class->SetObjectSize(..) + // because Object::SizeOf delegates to Array::SizeOf + + // All arrays have java/lang/Cloneable and java/io/Serializable as + // interfaces. We need to set that up here, so that stuff like + // "instanceof" works right. + + // Use the single, global copies of "interfaces" and "iftable" + // (remember not to free them for arrays). + { + ObjPtr array_iftable = GetArrayIfTable(); + CHECK(array_iftable != nullptr); + array_class->SetIfTable(array_iftable); + } + + // Inherit access flags from the component type. + int access_flags = component_type->GetAccessFlags(); + // Lose any implementation detail flags; in particular, arrays aren't finalizable. + access_flags &= kAccJavaFlagsMask; + // Arrays can't be used as a superclass or interface, so we want to add "abstract final" + // and remove "interface". + access_flags |= kAccAbstract | kAccFinal; + access_flags &= ~kAccInterface; + // Arrays are access-checks-clean and preverified. + access_flags |= kAccVerificationAttempted; + + array_class->SetAccessFlagsDuringLinking(access_flags); + + // Array classes are fully initialized either during single threaded startup, + // or from a pre-fence visitor, so visibly initialized. + array_class->SetStatusForPrimitiveOrArray(ClassStatus::kVisiblyInitialized); +} + +void ClassLinker::FinishCoreArrayClassSetup(ClassRoot array_root) { + // Do not hold lock on the array class object, the initialization of + // core array classes is done while the process is still single threaded. + ObjPtr array_class = GetClassRoot(array_root, this); + FinishArrayClassSetup(array_class); + + std::string temp; + const char* descriptor = array_class->GetDescriptor(&temp); + size_t hash = ComputeModifiedUtf8Hash(descriptor); + ObjPtr existing = InsertClass(descriptor, array_class, hash); + CHECK(existing == nullptr); +} + +ObjPtr> ClassLinker::AllocStackTraceElementArray( + Thread* self, + size_t length) { + return mirror::ObjectArray::Alloc( + self, GetClassRoot>(this), length); +} + +ObjPtr ClassLinker::EnsureResolved(Thread* self, + const char* descriptor, + ObjPtr klass) { + DCHECK(klass != nullptr); + if (kIsDebugBuild) { + StackHandleScope<1> hs(self); + HandleWrapperObjPtr h = hs.NewHandleWrapper(&klass); + Thread::PoisonObjectPointersIfDebug(); + } + + // For temporary classes we must wait for them to be retired. + if (init_done_ && klass->IsTemp()) { + CHECK(!klass->IsResolved()); + if (klass->IsErroneousUnresolved()) { + ThrowEarlierClassFailure(klass); + return nullptr; + } + StackHandleScope<1> hs(self); + Handle h_class(hs.NewHandle(klass)); + ObjectLock lock(self, h_class); + // Loop and wait for the resolving thread to retire this class. + while (!h_class->IsRetired() && !h_class->IsErroneousUnresolved()) { + lock.WaitIgnoringInterrupts(); + } + if (h_class->IsErroneousUnresolved()) { + ThrowEarlierClassFailure(h_class.Get()); + return nullptr; + } + CHECK(h_class->IsRetired()); + // Get the updated class from class table. + klass = LookupClass(self, descriptor, h_class.Get()->GetClassLoader()); + } + + // Wait for the class if it has not already been linked. + size_t index = 0; + // Maximum number of yield iterations until we start sleeping. + static const size_t kNumYieldIterations = 1000; + // How long each sleep is in us. + static const size_t kSleepDurationUS = 1000; // 1 ms. + while (!klass->IsResolved() && !klass->IsErroneousUnresolved()) { + StackHandleScope<1> hs(self); + HandleWrapperObjPtr h_class(hs.NewHandleWrapper(&klass)); + { + ObjectTryLock lock(self, h_class); + // Can not use a monitor wait here since it may block when returning and deadlock if another + // thread has locked klass. + if (lock.Acquired()) { + // Check for circular dependencies between classes, the lock is required for SetStatus. + if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) { + ThrowClassCircularityError(h_class.Get()); + mirror::Class::SetStatus(h_class, ClassStatus::kErrorUnresolved, self); + return nullptr; + } + } + } + { + // Handle wrapper deals with klass moving. + ScopedThreadSuspension sts(self, kSuspended); + if (index < kNumYieldIterations) { + sched_yield(); + } else { + usleep(kSleepDurationUS); + } + } + ++index; + } + + if (klass->IsErroneousUnresolved()) { + ThrowEarlierClassFailure(klass); + return nullptr; + } + // Return the loaded class. No exceptions should be pending. + CHECK(klass->IsResolved()) << klass->PrettyClass(); + self->AssertNoPendingException(); + return klass; +} + +using ClassPathEntry = std::pair; + +// Search a collection of DexFiles for a descriptor +ClassPathEntry FindInClassPath(const char* descriptor, + size_t hash, const std::vector& class_path) { + for (const DexFile* dex_file : class_path) { + DCHECK(dex_file != nullptr); + const dex::ClassDef* dex_class_def = OatDexFile::FindClassDef(*dex_file, descriptor, hash); + if (dex_class_def != nullptr) { + return ClassPathEntry(dex_file, dex_class_def); + } + } + return ClassPathEntry(nullptr, nullptr); +} + +bool ClassLinker::FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa, + Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader, + /*out*/ ObjPtr* result) { + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr raw_shared_libraries = field->GetObject(class_loader.Get()); + if (raw_shared_libraries == nullptr) { + return true; + } + + StackHandleScope<2> hs(self); + Handle> shared_libraries( + hs.NewHandle(raw_shared_libraries->AsObjectArray())); + MutableHandle temp_loader = hs.NewHandle(nullptr); + for (auto loader : shared_libraries.Iterate()) { + temp_loader.Assign(loader); + if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, temp_loader, result)) { + return false; // One of the shared libraries is not supported. + } + if (*result != nullptr) { + return true; // Found the class up the chain. + } + } + return true; +} + +bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader, + /*out*/ ObjPtr* result) { + // Termination case: boot class loader. + if (IsBootClassLoader(soa, class_loader.Get())) { + *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash); + return true; + } + + if (IsPathOrDexClassLoader(soa, class_loader) || IsInMemoryDexClassLoader(soa, class_loader)) { + // For regular path or dex class loader the search order is: + // - parent + // - shared libraries + // - class loader dex files + + // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). + StackHandleScope<1> hs(self); + Handle h_parent(hs.NewHandle(class_loader->GetParent())); + if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result)) { + return false; // One of the parents is not supported. + } + if (*result != nullptr) { + return true; // Found the class up the chain. + } + + if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) { + return false; // One of the shared library loader is not supported. + } + if (*result != nullptr) { + return true; // Found the class in a shared library. + } + + // Search the current class loader classpath. + *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader); + return !soa.Self()->IsExceptionPending(); + } + + if (IsDelegateLastClassLoader(soa, class_loader)) { + // For delegate last, the search order is: + // - boot class path + // - shared libraries + // - class loader dex files + // - parent + *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash); + if (*result != nullptr) { + return true; // The class is part of the boot class path. + } + if (self->IsExceptionPending()) { + // Pending exception means there was an error other than ClassNotFound that must be returned + // to the caller. + return false; + } + + if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) { + return false; // One of the shared library loader is not supported. + } + if (*result != nullptr) { + return true; // Found the class in a shared library. + } + + *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader); + if (*result != nullptr) { + return true; // Found the class in the current class loader + } + if (self->IsExceptionPending()) { + // Pending exception means there was an error other than ClassNotFound that must be returned + // to the caller. + return false; + } + + // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). + StackHandleScope<1> hs(self); + Handle h_parent(hs.NewHandle(class_loader->GetParent())); + return FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result); + } + + // Unsupported class loader. + *result = nullptr; + return false; +} + +namespace { + +// Matches exceptions caught in DexFile.defineClass. +ALWAYS_INLINE bool MatchesDexFileCaughtExceptions(ObjPtr throwable, + ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + return + // ClassNotFoundException. + throwable->InstanceOf(GetClassRoot(ClassRoot::kJavaLangClassNotFoundException, + class_linker)) + || + // NoClassDefFoundError. TODO: Reconsider this. b/130746382. + throwable->InstanceOf(Runtime::Current()->GetPreAllocatedNoClassDefFoundError()->GetClass()); +} + +// Clear exceptions caught in DexFile.defineClass. +ALWAYS_INLINE void FilterDexFileCaughtExceptions(Thread* self, ClassLinker* class_linker) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (MatchesDexFileCaughtExceptions(self->GetException(), class_linker)) { + self->ClearException(); + } +} + +} // namespace + +// Finds the class in the boot class loader. +// If the class is found the method returns the resolved class. Otherwise it returns null. +ObjPtr ClassLinker::FindClassInBootClassLoaderClassPath(Thread* self, + const char* descriptor, + size_t hash) { + ObjPtr result = nullptr; + ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); + if (pair.second != nullptr) { + ObjPtr klass = LookupClass(self, descriptor, hash, nullptr); + if (klass != nullptr) { + result = EnsureResolved(self, descriptor, klass); + } else { + result = DefineClass(self, + descriptor, + hash, + ScopedNullHandle(), + *pair.first, + *pair.second); + } + if (result == nullptr) { + CHECK(self->IsExceptionPending()) << descriptor; + FilterDexFileCaughtExceptions(self, this); + } + } + return result; +} + +ObjPtr ClassLinker::FindClassInBaseDexClassLoaderClassPath( + ScopedObjectAccessAlreadyRunnable& soa, + const char* descriptor, + size_t hash, + Handle class_loader) { + DCHECK(IsPathOrDexClassLoader(soa, class_loader) || + IsInMemoryDexClassLoader(soa, class_loader) || + IsDelegateLastClassLoader(soa, class_loader)) + << "Unexpected class loader for descriptor " << descriptor; + + const DexFile* dex_file = nullptr; + const dex::ClassDef* class_def = nullptr; + ObjPtr ret; + auto find_class_def = [&](const DexFile* cp_dex_file) REQUIRES_SHARED(Locks::mutator_lock_) { + const dex::ClassDef* cp_class_def = OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash); + if (cp_class_def != nullptr) { + dex_file = cp_dex_file; + class_def = cp_class_def; + return false; // Found a class definition, stop visit. + } + return true; // Continue with the next DexFile. + }; + VisitClassLoaderDexFiles(soa, class_loader, find_class_def); + + ObjPtr klass = nullptr; + if (class_def != nullptr) { + klass = DefineClass(soa.Self(), descriptor, hash, class_loader, *dex_file, *class_def); + if (UNLIKELY(klass == nullptr)) { + CHECK(soa.Self()->IsExceptionPending()) << descriptor; + FilterDexFileCaughtExceptions(soa.Self(), this); + } else { + DCHECK(!soa.Self()->IsExceptionPending()); + } + } + return klass; +} + +ObjPtr ClassLinker::FindClass(Thread* self, + const char* descriptor, + Handle class_loader) { + DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; + DCHECK(self != nullptr); + self->AssertNoPendingException(); + self->PoisonObjectPointers(); // For DefineClass, CreateArrayClass, etc... + if (descriptor[1] == '\0') { + // only the descriptors of primitive types should be 1 character long, also avoid class lookup + // for primitive classes that aren't backed by dex files. + return FindPrimitiveClass(descriptor[0]); + } + const size_t hash = ComputeModifiedUtf8Hash(descriptor); + // Find the class in the loaded classes table. + ObjPtr klass = LookupClass(self, descriptor, hash, class_loader.Get()); + if (klass != nullptr) { + return EnsureResolved(self, descriptor, klass); + } + // Class is not yet loaded. + if (descriptor[0] != '[' && class_loader == nullptr) { + // Non-array class and the boot class loader, search the boot class path. + ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); + if (pair.second != nullptr) { + return DefineClass(self, + descriptor, + hash, + ScopedNullHandle(), + *pair.first, + *pair.second); + } else { + // The boot class loader is searched ahead of the application class loader, failures are + // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to + // trigger the chaining with a proper stack trace. + ObjPtr pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return nullptr; + } + } + ObjPtr result_ptr; + bool descriptor_equals; + if (descriptor[0] == '[') { + result_ptr = CreateArrayClass(self, descriptor, hash, class_loader); + DCHECK_EQ(result_ptr == nullptr, self->IsExceptionPending()); + DCHECK(result_ptr == nullptr || result_ptr->DescriptorEquals(descriptor)); + descriptor_equals = true; + } else { + ScopedObjectAccessUnchecked soa(self); + bool known_hierarchy = + FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr); + if (result_ptr != nullptr) { + // The chain was understood and we found the class. We still need to add the class to + // the class table to protect from racy programs that can try and redefine the path list + // which would change the Class returned for subsequent evaluation of const-class. + DCHECK(known_hierarchy); + DCHECK(result_ptr->DescriptorEquals(descriptor)); + descriptor_equals = true; + } else if (!self->IsExceptionPending()) { + // Either the chain wasn't understood or the class wasn't found. + // If there is a pending exception we didn't clear, it is a not a ClassNotFoundException and + // we should return it instead of silently clearing and retrying. + // + // If the chain was understood but we did not find the class, let the Java-side + // rediscover all this and throw the exception with the right stack trace. Note that + // the Java-side could still succeed for racy programs if another thread is actively + // modifying the class loader's path list. + + // The runtime is not allowed to call into java from a runtime-thread so just abort. + if (self->IsRuntimeThread()) { + // Oops, we can't call into java so we can't run actual class-loader code. + // This is true for e.g. for the compiler (jit or aot). + ObjPtr pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return nullptr; + } + + // Inlined DescriptorToDot(descriptor) with extra validation. + // + // Throw NoClassDefFoundError early rather than potentially load a class only to fail + // the DescriptorEquals() check below and give a confusing error message. For example, + // when native code erroneously calls JNI GetFieldId() with signature "java/lang/String" + // instead of "Ljava/lang/String;", the message below using the "dot" names would be + // "class loader [...] returned class java.lang.String instead of java.lang.String". + size_t descriptor_length = strlen(descriptor); + if (UNLIKELY(descriptor[0] != 'L') || + UNLIKELY(descriptor[descriptor_length - 1] != ';') || + UNLIKELY(memchr(descriptor + 1, '.', descriptor_length - 2) != nullptr)) { + ThrowNoClassDefFoundError("Invalid descriptor: %s.", descriptor); + return nullptr; + } + + std::string class_name_string(descriptor + 1, descriptor_length - 2); + std::replace(class_name_string.begin(), class_name_string.end(), '/', '.'); + if (known_hierarchy && + fast_class_not_found_exceptions_ && + !Runtime::Current()->IsJavaDebuggable()) { + // For known hierarchy, we know that the class is going to throw an exception. If we aren't + // debuggable, optimize this path by throwing directly here without going back to Java + // language. This reduces how many ClassNotFoundExceptions happen. + self->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;", + "%s", + class_name_string.c_str()); + } else { + ScopedLocalRef class_loader_object( + soa.Env(), soa.AddLocalReference(class_loader.Get())); + ScopedLocalRef result(soa.Env(), nullptr); + { + ScopedThreadStateChange tsc(self, kNative); + ScopedLocalRef class_name_object( + soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str())); + if (class_name_object.get() == nullptr) { + DCHECK(self->IsExceptionPending()); // OOME. + return nullptr; + } + CHECK(class_loader_object.get() != nullptr); + result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(), + WellKnownClasses::java_lang_ClassLoader_loadClass, + class_name_object.get())); + } + if (result.get() == nullptr && !self->IsExceptionPending()) { + // broken loader - throw NPE to be compatible with Dalvik + ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s", + class_name_string.c_str()).c_str()); + return nullptr; + } + result_ptr = soa.Decode(result.get()); + // Check the name of the returned class. + descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor); + } + } else { + DCHECK(!MatchesDexFileCaughtExceptions(self->GetException(), this)); + } + } + + if (self->IsExceptionPending()) { + // If the ClassLoader threw or array class allocation failed, pass that exception up. + // However, to comply with the RI behavior, first check if another thread succeeded. + result_ptr = LookupClass(self, descriptor, hash, class_loader.Get()); + if (result_ptr != nullptr && !result_ptr->IsErroneous()) { + self->ClearException(); + return EnsureResolved(self, descriptor, result_ptr); + } + return nullptr; + } + + // Try to insert the class to the class table, checking for mismatch. + ObjPtr old; + { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get()); + old = class_table->Lookup(descriptor, hash); + if (old == nullptr) { + old = result_ptr; // For the comparison below, after releasing the lock. + if (descriptor_equals) { + class_table->InsertWithHash(result_ptr, hash); + WriteBarrier::ForEveryFieldWrite(class_loader.Get()); + } // else throw below, after releasing the lock. + } + } + if (UNLIKELY(old != result_ptr)) { + // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel + // capable class loaders. (All class loaders are considered parallel capable on Android.) + ObjPtr loader_class = class_loader->GetClass(); + const char* loader_class_name = + loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex()); + LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name) + << " is not well-behaved; it returned a different Class for racing loadClass(\"" + << DescriptorToDot(descriptor) << "\")."; + return EnsureResolved(self, descriptor, old); + } + if (UNLIKELY(!descriptor_equals)) { + std::string result_storage; + const char* result_name = result_ptr->GetDescriptor(&result_storage); + std::string loader_storage; + const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage); + ThrowNoClassDefFoundError( + "Initiating class loader of type %s returned class %s instead of %s.", + DescriptorToDot(loader_class_name).c_str(), + DescriptorToDot(result_name).c_str(), + DescriptorToDot(descriptor).c_str()); + return nullptr; + } + // Success. + return result_ptr; +} + +// Helper for maintaining DefineClass counting. We need to notify callbacks when we start/end a +// define-class and how many recursive DefineClasses we are at in order to allow for doing things +// like pausing class definition. +struct ScopedDefiningClass { + public: + explicit ScopedDefiningClass(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) + : self_(self), returned_(false) { + Locks::mutator_lock_->AssertSharedHeld(self_); + Runtime::Current()->GetRuntimeCallbacks()->BeginDefineClass(); + self_->IncrDefineClassCount(); + } + ~ScopedDefiningClass() REQUIRES_SHARED(Locks::mutator_lock_) { + Locks::mutator_lock_->AssertSharedHeld(self_); + CHECK(returned_); + } + + ObjPtr Finish(Handle h_klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(!returned_); + self_->DecrDefineClassCount(); + Runtime::Current()->GetRuntimeCallbacks()->EndDefineClass(); + Thread::PoisonObjectPointersIfDebug(); + returned_ = true; + return h_klass.Get(); + } + + ObjPtr Finish(ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self_); + Handle h_klass(hs.NewHandle(klass)); + return Finish(h_klass); + } + + ObjPtr Finish(nullptr_t np ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedNullHandle snh; + return Finish(snh); + } + + private: + Thread* self_; + bool returned_; +}; + +ObjPtr ClassLinker::DefineClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader, + const DexFile& dex_file, + const dex::ClassDef& dex_class_def) { + ScopedDefiningClass sdc(self); + StackHandleScope<3> hs(self); + metrics::AutoTimer timer{GetMetrics()->ClassLoadingTotalTime()}; + auto klass = hs.NewHandle(nullptr); + + +// std::string cmdlinePath="/proc/self/cmdline"; +// auto cmdlineData = std::string(); +// if(ReadFileToString(cmdlinePath,&cmdlineData)){ +// if(!strstr(cmdlineData.c_str(),"android") && !strstr(cmdlineData.c_str(),"google") +// &&!strstr(cmdlineData.c_str(),"zygote") &&!strstr(cmdlineData.c_str(),"system_server")){ +// if(cmdlineData.length()>0){ +// char savePath[100]={0}; +// ALOGD("mikrom DefineClass write 1 %s dex begin:%p size:%zu\n",cmdlineData.c_str(),dex_file.Begin(),dex_file.Size()); +// sprintf(savePath, "/data/data/%s/defineClass_%zu", cmdlineData.c_str(),dex_file.Size()); +// ALOGD("mikrom DefineClass write 2 %s dex begin:%p size:%zu\n",savePath,dex_file.Begin(),dex_file.Size()); +// if(access(savePath, F_OK) != 0){ +// if (!WriteStringToFile(std::string((const char*)dex_file.Begin(), dex_file.Size()), savePath)) { +// // 写入失败 +// ALOGD("mikrom DefineClass dex begin:%p size:%zu write err\n",dex_file.Begin(),dex_file.Size()); +// +// } +// } +// } +// } +// } + +// ALOGD("mikrom DefineClass dex begin:%p size:%zu\n",dex_file.Begin(),dex_file.Size()); + // Load the class from the dex file. + if (UNLIKELY(!init_done_)) { + // finish up init of hand crafted class_roots_ + if (strcmp(descriptor, "Ljava/lang/Object;") == 0) { + klass.Assign(GetClassRoot(this)); + } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) { + klass.Assign(GetClassRoot(this)); + } else if (strcmp(descriptor, "Ljava/lang/String;") == 0) { + klass.Assign(GetClassRoot(this)); + } else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) { + klass.Assign(GetClassRoot(this)); + } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) { + klass.Assign(GetClassRoot(this)); + } else if (strcmp(descriptor, "Ldalvik/system/ClassExt;") == 0) { + klass.Assign(GetClassRoot(this)); + } + } + + // For AOT-compilation of an app, we may use a shortened boot class path that excludes + // some runtime modules. Prevent definition of classes in app class loader that could clash + // with these modules as these classes could be resolved differently during execution. + if (class_loader != nullptr && + Runtime::Current()->IsAotCompiler() && + IsUpdatableBootClassPathDescriptor(descriptor)) { + ObjPtr pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return sdc.Finish(nullptr); + } + + // For AOT-compilation of an app, we may use only a public SDK to resolve symbols. If the SDK + // checks are configured (a non null SdkChecker) and the descriptor is not in the provided + // public class path then we prevent the definition of the class. + // + // NOTE that we only do the checks for the boot classpath APIs. Anything else, like the app + // classpath is not checked. + if (class_loader == nullptr && + Runtime::Current()->IsAotCompiler() && + DenyAccessBasedOnPublicSdk(descriptor)) { + ObjPtr pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return sdc.Finish(nullptr); + } + + // This is to prevent the calls to ClassLoad and ClassPrepare which can cause java/user-supplied + // code to be executed. We put it up here so we can avoid all the allocations associated with + // creating the class. This can happen with (eg) jit threads. + if (!self->CanLoadClasses()) { + // Make sure we don't try to load anything, potentially causing an infinite loop. + ObjPtr pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return sdc.Finish(nullptr); + } + + if (klass == nullptr) { + // Allocate a class with the status of not ready. + // Interface object should get the right size here. Regular class will + // figure out the right size later and be replaced with one of the right + // size when the class becomes resolved. + if (CanAllocClass()) { + klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def))); + } else { + return sdc.Finish(nullptr); + } + } + if (UNLIKELY(klass == nullptr)) { + self->AssertPendingOOMException(); + return sdc.Finish(nullptr); + } + // Get the real dex file. This will return the input if there aren't any callbacks or they do + // nothing. + DexFile const* new_dex_file = nullptr; + dex::ClassDef const* new_class_def = nullptr; + // TODO We should ideally figure out some way to move this after we get a lock on the klass so it + // will only be called once. + Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor, + klass, + class_loader, + dex_file, + dex_class_def, + &new_dex_file, + &new_class_def); + // Check to see if an exception happened during runtime callbacks. Return if so. + if (self->IsExceptionPending()) { + return sdc.Finish(nullptr); + } + ObjPtr dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get()); + if (dex_cache == nullptr) { + self->AssertPendingException(); + return sdc.Finish(nullptr); + } + klass->SetDexCache(dex_cache); + SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get()); + + // Mark the string class by setting its access flag. + if (UNLIKELY(!init_done_)) { + if (strcmp(descriptor, "Ljava/lang/String;") == 0) { + klass->SetStringClass(); + } + } + + ObjectLock lock(self, klass); + klass->SetClinitThreadId(self->GetTid()); + // Make sure we have a valid empty iftable even if there are errors. + klass->SetIfTable(GetClassRoot(this)->GetIfTable()); + + // Add the newly loaded class to the loaded classes table. + ObjPtr existing = InsertClass(descriptor, klass.Get(), hash); + if (existing != nullptr) { + // We failed to insert because we raced with another thread. Calling EnsureResolved may cause + // this thread to block. + return sdc.Finish(EnsureResolved(self, descriptor, existing)); + } + + // Load the fields and other things after we are inserted in the table. This is so that we don't + // end up allocating unfree-able linear alloc resources and then lose the race condition. The + // other reason is that the field roots are only visited from the class table. So we need to be + // inserted before we allocate / fill in these fields. + LoadClass(self, *new_dex_file, *new_class_def, klass); + if (self->IsExceptionPending()) { + VLOG(class_linker) << self->GetException()->Dump(); + // An exception occured during load, set status to erroneous while holding klass' lock in case + // notification is necessary. + if (!klass->IsErroneous()) { + mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self); + } + return sdc.Finish(nullptr); + } + + // Finish loading (if necessary) by finding parents + CHECK(!klass->IsLoaded()); + if (!LoadSuperAndInterfaces(klass, *new_dex_file)) { + // Loading failed. + if (!klass->IsErroneous()) { + mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self); + } + return sdc.Finish(nullptr); + } + CHECK(klass->IsLoaded()); + + // At this point the class is loaded. Publish a ClassLoad event. + // Note: this may be a temporary class. It is a listener's responsibility to handle this. + Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(klass); + + // Link the class (if necessary) + CHECK(!klass->IsResolved()); + // TODO: Use fast jobjects? + auto interfaces = hs.NewHandle>(nullptr); + + MutableHandle h_new_class = hs.NewHandle(nullptr); + if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) { + // Linking failed. + if (!klass->IsErroneous()) { + mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self); + } + return sdc.Finish(nullptr); + } + self->AssertNoPendingException(); + CHECK(h_new_class != nullptr) << descriptor; + CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor; + + // Instrumentation may have updated entrypoints for all methods of all + // classes. However it could not update methods of this class while we + // were loading it. Now the class is resolved, we can update entrypoints + // as required by instrumentation. + if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { + // We must be in the kRunnable state to prevent instrumentation from + // suspending all threads to update entrypoints while we are doing it + // for this class. + DCHECK_EQ(self->GetState(), kRunnable); + Runtime::Current()->GetInstrumentation()->InstallStubsForClass(h_new_class.Get()); + } + + /* + * We send CLASS_PREPARE events to the debugger from here. The + * definition of "preparation" is creating the static fields for a + * class and initializing them to the standard default values, but not + * executing any code (that comes later, during "initialization"). + * + * We did the static preparation in LinkClass. + * + * The class has been prepared and resolved but possibly not yet verified + * at this point. + */ + Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(klass, h_new_class); + + // Notify native debugger of the new class and its layout. + jit::Jit::NewTypeLoadedIfUsingJit(h_new_class.Get()); + + return sdc.Finish(h_new_class); +} + +uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, + const dex::ClassDef& dex_class_def) { + size_t num_ref = 0; + size_t num_8 = 0; + size_t num_16 = 0; + size_t num_32 = 0; + size_t num_64 = 0; + ClassAccessor accessor(dex_file, dex_class_def); + // We allow duplicate definitions of the same field in a class_data_item + // but ignore the repeated indexes here, b/21868015. + uint32_t last_field_idx = dex::kDexNoIndex; + for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { + uint32_t field_idx = field.GetIndex(); + // Ordering enforced by DexFileVerifier. + DCHECK(last_field_idx == dex::kDexNoIndex || last_field_idx <= field_idx); + if (UNLIKELY(field_idx == last_field_idx)) { + continue; + } + last_field_idx = field_idx; + const dex::FieldId& field_id = dex_file.GetFieldId(field_idx); + const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id); + char c = descriptor[0]; + switch (c) { + case 'L': + case '[': + num_ref++; + break; + case 'J': + case 'D': + num_64++; + break; + case 'I': + case 'F': + num_32++; + break; + case 'S': + case 'C': + num_16++; + break; + case 'B': + case 'Z': + num_8++; + break; + default: + LOG(FATAL) << "Unknown descriptor: " << c; + UNREACHABLE(); + } + } + return mirror::Class::ComputeClassSize(false, + 0, + num_8, + num_16, + num_32, + num_64, + num_ref, + image_pointer_size_); +} + +// Special case to get oat code without overwriting a trampoline. +const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) { + CHECK(method->IsInvokable()) << method->PrettyMethod(); + if (method->IsProxyMethod()) { + return GetQuickProxyInvokeHandler(); + } + const void* code = method->GetOatMethodQuickCode(GetImagePointerSize()); + if (code != nullptr) { + return code; + } + + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + code = jit->GetCodeCache()->GetSavedEntryPointOfPreCompiledMethod(method); + if (code != nullptr) { + return code; + } + } + + if (method->IsNative()) { + // No code and native? Use generic trampoline. + return GetQuickGenericJniStub(); + } + + if (interpreter::CanRuntimeUseNterp() && CanMethodUseNterp(method)) { + return interpreter::GetNterpEntryPoint(); + } + + return GetQuickToInterpreterBridge(); +} + +bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) { + ScopedAssertNoThreadSuspension sants(__FUNCTION__); + if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) { + return false; + } + + if (quick_code == nullptr) { + return true; + } + + Runtime* runtime = Runtime::Current(); + instrumentation::Instrumentation* instr = runtime->GetInstrumentation(); + if (instr->InterpretOnly()) { + return true; + } + + if (runtime->GetClassLinker()->IsQuickToInterpreterBridge(quick_code)) { + // Doing this check avoids doing compiled/interpreter transitions. + return true; + } + + if (Thread::Current()->IsForceInterpreter()) { + // Force the use of interpreter when it is required by the debugger. + return true; + } + + if (Thread::Current()->IsAsyncExceptionPending()) { + // Force use of interpreter to handle async-exceptions + return true; + } + + if (quick_code == GetQuickInstrumentationEntryPoint()) { + const void* instr_target = instr->GetCodeForInvoke(method); + DCHECK_NE(instr_target, GetQuickInstrumentationEntryPoint()) << method->PrettyMethod(); + return ShouldUseInterpreterEntrypoint(method, instr_target); + } + + if (runtime->IsJavaDebuggable()) { + // For simplicity, we ignore precompiled code and go to the interpreter + // assuming we don't already have jitted code. + // We could look at the oat file where `quick_code` is being defined, + // and check whether it's been compiled debuggable, but we decided to + // only rely on the JIT for debuggable apps. + jit::Jit* jit = Runtime::Current()->GetJit(); + return (jit == nullptr) || !jit->GetCodeCache()->ContainsPc(quick_code); + } + + if (runtime->IsNativeDebuggable()) { + DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse()); + // If we are doing native debugging, ignore application's AOT code, + // since we want to JIT it (at first use) with extra stackmaps for native + // debugging. We keep however all AOT code from the boot image, + // since the JIT-at-first-use is blocking and would result in non-negligible + // startup performance impact. + return !runtime->GetHeap()->IsInBootImageOatFile(quick_code); + } + + return false; +} + +void ClassLinker::FixupStaticTrampolines(Thread* self, ObjPtr klass) { + ScopedAssertNoThreadSuspension sants(__FUNCTION__); + DCHECK(klass->IsVisiblyInitialized()) << klass->PrettyDescriptor(); + size_t num_direct_methods = klass->NumDirectMethods(); + if (num_direct_methods == 0) { + return; // No direct methods => no static methods. + } + if (UNLIKELY(klass->IsProxyClass())) { + return; + } + PointerSize pointer_size = image_pointer_size_; + if (std::any_of(klass->GetDirectMethods(pointer_size).begin(), + klass->GetDirectMethods(pointer_size).end(), + [](const ArtMethod& m) { return m.IsCriticalNative(); })) { + // Store registered @CriticalNative methods, if any, to JNI entrypoints. + // Direct methods are a contiguous chunk of memory, so use the ordering of the map. + ArtMethod* first_method = klass->GetDirectMethod(0u, pointer_size); + ArtMethod* last_method = klass->GetDirectMethod(num_direct_methods - 1u, pointer_size); + MutexLock lock(self, critical_native_code_with_clinit_check_lock_); + auto lb = critical_native_code_with_clinit_check_.lower_bound(first_method); + while (lb != critical_native_code_with_clinit_check_.end() && lb->first <= last_method) { + lb->first->SetEntryPointFromJni(lb->second); + lb = critical_native_code_with_clinit_check_.erase(lb); + } + } + Runtime* runtime = Runtime::Current(); + if (!runtime->IsStarted()) { + if (runtime->IsAotCompiler() || runtime->GetHeap()->HasBootImageSpace()) { + return; // OAT file unavailable. + } + } + + const DexFile& dex_file = klass->GetDexFile(); + bool has_oat_class; + OatFile::OatClass oat_class = OatFile::FindOatClass(dex_file, + klass->GetDexClassDefIndex(), + &has_oat_class); + // Link the code of methods skipped by LinkCode. + for (size_t method_index = 0; method_index < num_direct_methods; ++method_index) { + ArtMethod* method = klass->GetDirectMethod(method_index, pointer_size); + if (!method->IsStatic()) { + // Only update static methods. + continue; + } + const void* quick_code = nullptr; + + // In order: + // 1) Check if we have AOT Code. + // 2) Check if we have JIT Code. + // 3) Check if we can use Nterp. + if (has_oat_class) { + OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index); + quick_code = oat_method.GetQuickCode(); + } + + jit::Jit* jit = runtime->GetJit(); + if (quick_code == nullptr && jit != nullptr) { + quick_code = jit->GetCodeCache()->GetSavedEntryPointOfPreCompiledMethod(method); + } + + if (quick_code == nullptr && + interpreter::CanRuntimeUseNterp() && + CanMethodUseNterp(method)) { + quick_code = interpreter::GetNterpEntryPoint(); + } + + // Check whether the method is native, in which case it's generic JNI. + if (quick_code == nullptr && method->IsNative()) { + quick_code = GetQuickGenericJniStub(); + } else if (ShouldUseInterpreterEntrypoint(method, quick_code)) { + // Use interpreter entry point. + if (IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode())) { + // If we have the trampoline or the bridge already, no need to update. + // This saves in not dirtying boot image memory. + continue; + } + quick_code = GetQuickToInterpreterBridge(); + } + CHECK(quick_code != nullptr); + runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code); + } + // Ignore virtual methods on the iterator. +} + +// Does anything needed to make sure that the compiler will not generate a direct invoke to this +// method. Should only be called on non-invokable methods. +inline void EnsureThrowsInvocationError(ClassLinker* class_linker, ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(method != nullptr); + DCHECK(!method->IsInvokable()); + method->SetEntryPointFromQuickCompiledCodePtrSize( + class_linker->GetQuickToInterpreterBridgeTrampoline(), + class_linker->GetImagePointerSize()); +} + +static void LinkCode(ClassLinker* class_linker, + ArtMethod* method, + const OatFile::OatClass* oat_class, + uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedAssertNoThreadSuspension sants(__FUNCTION__); + Runtime* const runtime = Runtime::Current(); + if (runtime->IsAotCompiler()) { + // The following code only applies to a non-compiler runtime. + return; + } + + // Method shouldn't have already been linked. + DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr); + + if (!method->IsInvokable()) { + EnsureThrowsInvocationError(class_linker, method); + return; + } + + const void* quick_code = nullptr; + if (oat_class != nullptr) { + // Every kind of method should at least get an invoke stub from the oat_method. + // non-abstract methods also get their code pointers. + const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index); + quick_code = oat_method.GetQuickCode(); + } + + bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code); + + // Note: this mimics the logic in image_writer.cc that installs the resolution + // stub only if we have compiled code and the method needs a class initialization + // check. + if (quick_code == nullptr) { + method->SetEntryPointFromQuickCompiledCode( + method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge()); + } else if (enter_interpreter) { + method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } else if (NeedsClinitCheckBeforeCall(method)) { + DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized()); // Actually ClassStatus::Idx. + // If we do have code but the method needs a class initialization check before calling + // that code, install the resolution stub that will perform the check. + // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines + // after initializing class (see ClassLinker::InitializeClass method). + method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub()); + } else { + method->SetEntryPointFromQuickCompiledCode(quick_code); + } + + if (method->IsNative()) { + // Set up the dlsym lookup stub. Do not go through `UnregisterNative()` + // as the extra processing for @CriticalNative is not needed yet. + method->SetEntryPointFromJni( + method->IsCriticalNative() ? GetJniDlsymLookupCriticalStub() : GetJniDlsymLookupStub()); + + if (enter_interpreter || quick_code == nullptr) { + // We have a native method here without code. Then it should have the generic JNI + // trampoline as entrypoint. + // TODO: this doesn't handle all the cases where trampolines may be installed. + DCHECK(class_linker->IsQuickGenericJniStub(method->GetEntryPointFromQuickCompiledCode())); + } + } +} + +void ClassLinker::SetupClass(const DexFile& dex_file, + const dex::ClassDef& dex_class_def, + Handle klass, + ObjPtr class_loader) { + CHECK(klass != nullptr); + CHECK(klass->GetDexCache() != nullptr); + CHECK_EQ(ClassStatus::kNotReady, klass->GetStatus()); + const char* descriptor = dex_file.GetClassDescriptor(dex_class_def); + CHECK(descriptor != nullptr); + + klass->SetClass(GetClassRoot(this)); + uint32_t access_flags = dex_class_def.GetJavaAccessFlags(); + CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U); + klass->SetAccessFlagsDuringLinking(access_flags); + klass->SetClassLoader(class_loader); + DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot); + mirror::Class::SetStatus(klass, ClassStatus::kIdx, nullptr); + + klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def)); + klass->SetDexTypeIndex(dex_class_def.class_idx_); +} + +LengthPrefixedArray* ClassLinker::AllocArtFieldArray(Thread* self, + LinearAlloc* allocator, + size_t length) { + if (length == 0) { + return nullptr; + } + // If the ArtField alignment changes, review all uses of LengthPrefixedArray. + static_assert(alignof(ArtField) == 4, "ArtField alignment is expected to be 4."); + size_t storage_size = LengthPrefixedArray::ComputeSize(length); + void* array_storage = allocator->Alloc(self, storage_size); + auto* ret = new(array_storage) LengthPrefixedArray(length); + CHECK(ret != nullptr); + std::uninitialized_fill_n(&ret->At(0), length, ArtField()); + return ret; +} + +LengthPrefixedArray* ClassLinker::AllocArtMethodArray(Thread* self, + LinearAlloc* allocator, + size_t length) { + if (length == 0) { + return nullptr; + } + const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_); + const size_t method_size = ArtMethod::Size(image_pointer_size_); + const size_t storage_size = + LengthPrefixedArray::ComputeSize(length, method_size, method_alignment); + void* array_storage = allocator->Alloc(self, storage_size); + auto* ret = new (array_storage) LengthPrefixedArray(length); + CHECK(ret != nullptr); + for (size_t i = 0; i < length; ++i) { + new(reinterpret_cast(&ret->At(i, method_size, method_alignment))) ArtMethod; + } + return ret; +} + +LinearAlloc* ClassLinker::GetAllocatorForClassLoader(ObjPtr class_loader) { + if (class_loader == nullptr) { + return Runtime::Current()->GetLinearAlloc(); + } + LinearAlloc* allocator = class_loader->GetAllocator(); + DCHECK(allocator != nullptr); + return allocator; +} + +LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(ObjPtr class_loader) { + if (class_loader == nullptr) { + return Runtime::Current()->GetLinearAlloc(); + } + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + LinearAlloc* allocator = class_loader->GetAllocator(); + if (allocator == nullptr) { + RegisterClassLoader(class_loader); + allocator = class_loader->GetAllocator(); + CHECK(allocator != nullptr); + } + return allocator; +} + +void ClassLinker::LoadClass(Thread* self, + const DexFile& dex_file, + const dex::ClassDef& dex_class_def, + Handle klass) { + ClassAccessor accessor(dex_file, + dex_class_def, + /* parse_hiddenapi_class_data= */ klass->IsBootStrapClassLoaded()); + if (!accessor.HasClassData()) { + return; + } + Runtime* const runtime = Runtime::Current(); + { + // Note: We cannot have thread suspension until the field and method arrays are setup or else + // Class::VisitFieldRoots may miss some fields or methods. + ScopedAssertNoThreadSuspension nts(__FUNCTION__); + // Load static fields. + // We allow duplicate definitions of the same field in a class_data_item + // but ignore the repeated indexes here, b/21868015. + LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader()); + LengthPrefixedArray* sfields = AllocArtFieldArray(self, + allocator, + accessor.NumStaticFields()); + LengthPrefixedArray* ifields = AllocArtFieldArray(self, + allocator, + accessor.NumInstanceFields()); + size_t num_sfields = 0u; + size_t num_ifields = 0u; + uint32_t last_static_field_idx = 0u; + uint32_t last_instance_field_idx = 0u; + + // Methods + bool has_oat_class = false; + const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler()) + ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class) + : OatFile::OatClass::Invalid(); + const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr; + klass->SetMethodsPtr( + AllocArtMethodArray(self, allocator, accessor.NumMethods()), + accessor.NumDirectMethods(), + accessor.NumVirtualMethods()); + size_t class_def_method_index = 0; + uint32_t last_dex_method_index = dex::kDexNoIndex; + size_t last_class_def_method_index = 0; + + // Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the + // methods needs to decode all of the fields. + accessor.VisitFieldsAndMethods([&]( + const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t field_idx = field.GetIndex(); + DCHECK_GE(field_idx, last_static_field_idx); // Ordering enforced by DexFileVerifier. + if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) { + LoadField(field, klass, &sfields->At(num_sfields)); + ++num_sfields; + last_static_field_idx = field_idx; + } + }, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t field_idx = field.GetIndex(); + DCHECK_GE(field_idx, last_instance_field_idx); // Ordering enforced by DexFileVerifier. + if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) { + LoadField(field, klass, &ifields->At(num_ifields)); + ++num_ifields; + last_instance_field_idx = field_idx; + } + }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index, + image_pointer_size_); + LoadMethod(dex_file, method, klass, art_method); + LinkCode(this, art_method, oat_class_ptr, class_def_method_index); + uint32_t it_method_index = method.GetIndex(); + if (last_dex_method_index == it_method_index) { + // duplicate case + art_method->SetMethodIndex(last_class_def_method_index); + } else { + art_method->SetMethodIndex(class_def_method_index); + last_dex_method_index = it_method_index; + last_class_def_method_index = class_def_method_index; + } + ++class_def_method_index; + }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* art_method = klass->GetVirtualMethodUnchecked( + class_def_method_index - accessor.NumDirectMethods(), + image_pointer_size_); + LoadMethod(dex_file, method, klass, art_method); + LinkCode(this, art_method, oat_class_ptr, class_def_method_index); + ++class_def_method_index; + }); + + if (UNLIKELY(num_ifields + num_sfields != accessor.NumFields())) { + LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor() + << " (unique static fields: " << num_sfields << "/" << accessor.NumStaticFields() + << ", unique instance fields: " << num_ifields << "/" << accessor.NumInstanceFields() + << ")"; + // NOTE: Not shrinking the over-allocated sfields/ifields, just setting size. + if (sfields != nullptr) { + sfields->SetSize(num_sfields); + } + if (ifields != nullptr) { + ifields->SetSize(num_ifields); + } + } + // Set the field arrays. + klass->SetSFieldsPtr(sfields); + DCHECK_EQ(klass->NumStaticFields(), num_sfields); + klass->SetIFieldsPtr(ifields); + DCHECK_EQ(klass->NumInstanceFields(), num_ifields); + } + // Ensure that the card is marked so that remembered sets pick up native roots. + WriteBarrier::ForEveryFieldWrite(klass.Get()); + self->AllowThreadSuspension(); +} + +void ClassLinker::LoadField(const ClassAccessor::Field& field, + Handle klass, + ArtField* dst) { + const uint32_t field_idx = field.GetIndex(); + dst->SetDexFieldIndex(field_idx); + dst->SetDeclaringClass(klass.Get()); + + // Get access flags from the DexFile and set hiddenapi runtime access flags. + dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field)); +} + +void ClassLinker::LoadMethod(const DexFile& dex_file, + const ClassAccessor::Method& method, + Handle klass, + ArtMethod* dst) { + const uint32_t dex_method_idx = method.GetIndex(); + const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); + const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_); + + ScopedAssertNoThreadSuspension ants("LoadMethod"); + dst->SetDexMethodIndex(dex_method_idx); + dst->SetDeclaringClass(klass.Get()); + + // Get access flags from the DexFile and set hiddenapi runtime access flags. + uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method); + + if (UNLIKELY(strcmp("finalize", method_name) == 0)) { + // Set finalizable flag on declaring class. + if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) { + // Void return type. + if (klass->GetClassLoader() != nullptr) { // All non-boot finalizer methods are flagged. + klass->SetFinalizable(); + } else { + std::string temp; + const char* klass_descriptor = klass->GetDescriptor(&temp); + // The Enum class declares a "final" finalize() method to prevent subclasses from + // introducing a finalizer. We don't want to set the finalizable flag for Enum or its + // subclasses, so we exclude it here. + // We also want to avoid setting the flag on Object, where we know that finalize() is + // empty. + if (strcmp(klass_descriptor, "Ljava/lang/Object;") != 0 && + strcmp(klass_descriptor, "Ljava/lang/Enum;") != 0) { + klass->SetFinalizable(); + } + } + } + } else if (method_name[0] == '<') { + // Fix broken access flags for initializers. Bug 11157540. + bool is_init = (strcmp("", method_name) == 0); + bool is_clinit = !is_init && (strcmp("", method_name) == 0); + if (UNLIKELY(!is_init && !is_clinit)) { + LOG(WARNING) << "Unexpected '<' at start of method name " << method_name; + } else { + if (UNLIKELY((access_flags & kAccConstructor) == 0)) { + LOG(WARNING) << method_name << " didn't have expected constructor access flag in class " + << klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation(); + access_flags |= kAccConstructor; + } + } + } + if (UNLIKELY((access_flags & kAccNative) != 0u)) { + // Check if the native method is annotated with @FastNative or @CriticalNative. + access_flags |= annotations::GetNativeMethodAnnotationAccessFlags( + dex_file, dst->GetClassDef(), dex_method_idx); + } + dst->SetAccessFlags(access_flags); + // Must be done after SetAccessFlags since IsAbstract depends on it. + if (klass->IsInterface() && dst->IsAbstract()) { + dst->CalculateAndSetImtIndex(); + } + if (dst->HasCodeItem()) { + DCHECK_NE(method.GetCodeItemOffset(), 0u); + if (Runtime::Current()->IsAotCompiler()) { + dst->SetDataPtrSize(reinterpret_cast32(method.GetCodeItemOffset()), image_pointer_size_); + } else { + dst->SetCodeItem(dst->GetDexFile()->GetCodeItem(method.GetCodeItemOffset())); + } + } else { + dst->SetDataPtrSize(nullptr, image_pointer_size_); + DCHECK_EQ(method.GetCodeItemOffset(), 0u); + } + + // Set optimization flags related to the shorty. + const char* shorty = dst->GetShorty(); + bool all_parameters_are_reference = true; + bool all_parameters_are_reference_or_int = true; + bool return_type_is_fp = (shorty[0] == 'F' || shorty[0] == 'D'); + + for (size_t i = 1, e = strlen(shorty); i < e; ++i) { + if (shorty[i] != 'L') { + all_parameters_are_reference = false; + if (shorty[i] == 'F' || shorty[i] == 'D' || shorty[i] == 'J') { + all_parameters_are_reference_or_int = false; + break; + } + } + } + + if (!dst->IsNative() && all_parameters_are_reference) { + dst->SetNterpEntryPointFastPathFlag(); + } + + if (!return_type_is_fp && all_parameters_are_reference_or_int) { + dst->SetNterpInvokeFastPathFlag(); + } +} + +void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile* dex_file) { + ObjPtr dex_cache = AllocAndInitializeDexCache( + self, + *dex_file, + Runtime::Current()->GetLinearAlloc()); + CHECK(dex_cache != nullptr) << "Failed to allocate dex cache for " << dex_file->GetLocation(); + AppendToBootClassPath(dex_file, dex_cache); +} + +void ClassLinker::AppendToBootClassPath(const DexFile* dex_file, + ObjPtr dex_cache) { + CHECK(dex_file != nullptr); + CHECK(dex_cache != nullptr) << dex_file->GetLocation(); + boot_class_path_.push_back(dex_file); + WriterMutexLock mu(Thread::Current(), *Locks::dex_lock_); + RegisterDexFileLocked(*dex_file, dex_cache, /* class_loader= */ nullptr); +} + +void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, + ObjPtr dex_cache, + ObjPtr class_loader) { + Thread* const self = Thread::Current(); + Locks::dex_lock_->AssertExclusiveHeld(self); + CHECK(dex_cache != nullptr) << dex_file.GetLocation(); + CHECK_EQ(dex_cache->GetDexFile(), &dex_file) << dex_file.GetLocation(); + // For app images, the dex cache location may be a suffix of the dex file location since the + // dex file location is an absolute path. + const std::string dex_cache_location = dex_cache->GetLocation()->ToModifiedUtf8(); + const size_t dex_cache_length = dex_cache_location.length(); + CHECK_GT(dex_cache_length, 0u) << dex_file.GetLocation(); + std::string dex_file_location = dex_file.GetLocation(); + // The following paths checks don't work on preopt when using boot dex files, where the dex + // cache location is the one on device, and the dex_file's location is the one on host. + if (!(Runtime::Current()->IsAotCompiler() && class_loader == nullptr && !kIsTargetBuild)) { + CHECK_GE(dex_file_location.length(), dex_cache_length) + << dex_cache_location << " " << dex_file.GetLocation(); + const std::string dex_file_suffix = dex_file_location.substr( + dex_file_location.length() - dex_cache_length, + dex_cache_length); + // Example dex_cache location is SettingsProvider.apk and + // dex file location is /system/priv-app/SettingsProvider/SettingsProvider.apk + CHECK_EQ(dex_cache_location, dex_file_suffix); + } + const OatFile* oat_file = + (dex_file.GetOatDexFile() != nullptr) ? dex_file.GetOatDexFile()->GetOatFile() : nullptr; + // Clean up pass to remove null dex caches; null dex caches can occur due to class unloading + // and we are lazily removing null entries. Also check if we need to initialize OatFile data + // (.data.bimg.rel.ro and .bss sections) needed for code execution. + bool initialize_oat_file_data = (oat_file != nullptr) && oat_file->IsExecutable(); + JavaVMExt* const vm = self->GetJniEnv()->GetVm(); + for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) { + DexCacheData data = *it; + if (self->IsJWeakCleared(data.weak_root)) { + vm->DeleteWeakGlobalRef(self, data.weak_root); + it = dex_caches_.erase(it); + } else { + if (initialize_oat_file_data && + it->dex_file->GetOatDexFile() != nullptr && + it->dex_file->GetOatDexFile()->GetOatFile() == oat_file) { + initialize_oat_file_data = false; // Already initialized. + } + ++it; + } + } + if (initialize_oat_file_data) { + oat_file->InitializeRelocations(); + } + // Let hiddenapi assign a domain to the newly registered dex file. + hiddenapi::InitializeDexFileDomain(dex_file, class_loader); + + jweak dex_cache_jweak = vm->AddWeakGlobalRef(self, dex_cache); + DexCacheData data; + data.weak_root = dex_cache_jweak; + data.dex_file = dex_cache->GetDexFile(); + data.class_table = ClassTableForClassLoader(class_loader); + AddNativeDebugInfoForDex(self, data.dex_file); + DCHECK(data.class_table != nullptr); + // Make sure to hold the dex cache live in the class table. This case happens for the boot class + // path dex caches without an image. + data.class_table->InsertStrongRoot(dex_cache); + // Make sure that the dex cache holds the classloader live. + dex_cache->SetClassLoader(class_loader); + if (class_loader != nullptr) { + // Since we added a strong root to the class table, do the write barrier as required for + // remembered sets and generational GCs. + WriteBarrier::ForEveryFieldWrite(class_loader); + } + dex_caches_.push_back(data); +} + +ObjPtr ClassLinker::DecodeDexCacheLocked(Thread* self, const DexCacheData* data) { + return data != nullptr + ? ObjPtr::DownCast(self->DecodeJObject(data->weak_root)) + : nullptr; +} + +bool ClassLinker::IsSameClassLoader( + ObjPtr dex_cache, + const DexCacheData* data, + ObjPtr class_loader) { + CHECK(data != nullptr); + DCHECK_EQ(dex_cache->GetDexFile(), data->dex_file); + return data->class_table == ClassTableForClassLoader(class_loader); +} + +void ClassLinker::RegisterExistingDexCache(ObjPtr dex_cache, + ObjPtr class_loader) { + SCOPED_TRACE << __FUNCTION__ << " " << dex_cache->GetDexFile()->GetLocation(); + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + Handle h_dex_cache(hs.NewHandle(dex_cache)); + Handle h_class_loader(hs.NewHandle(class_loader)); + const DexFile* dex_file = dex_cache->GetDexFile(); + DCHECK(dex_file != nullptr) << "Attempt to register uninitialized dex_cache object!"; + if (kIsDebugBuild) { + ReaderMutexLock mu(self, *Locks::dex_lock_); + const DexCacheData* old_data = FindDexCacheDataLocked(*dex_file); + ObjPtr old_dex_cache = DecodeDexCacheLocked(self, old_data); + DCHECK(old_dex_cache.IsNull()) << "Attempt to manually register a dex cache thats already " + << "been registered on dex file " << dex_file->GetLocation(); + } + ClassTable* table; + { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + table = InsertClassTableForClassLoader(h_class_loader.Get()); + } + // Avoid a deadlock between a garbage collecting thread running a checkpoint, + // a thread holding the dex lock and blocking on a condition variable regarding + // weak references access, and a thread blocking on the dex lock. + gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseClassLinker, gc::kCollectorTypeClassLinker); + WriterMutexLock mu(self, *Locks::dex_lock_); + RegisterDexFileLocked(*dex_file, h_dex_cache.Get(), h_class_loader.Get()); + table->InsertStrongRoot(h_dex_cache.Get()); + if (h_class_loader.Get() != nullptr) { + // Since we added a strong root to the class table, do the write barrier as required for + // remembered sets and generational GCs. + WriteBarrier::ForEveryFieldWrite(h_class_loader.Get()); + } +} + +static void ThrowDexFileAlreadyRegisteredError(Thread* self, const DexFile& dex_file) + REQUIRES_SHARED(Locks::mutator_lock_) { + self->ThrowNewExceptionF("Ljava/lang/InternalError;", + "Attempt to register dex file %s with multiple class loaders", + dex_file.GetLocation().c_str()); +} + +ObjPtr ClassLinker::RegisterDexFile(const DexFile& dex_file, + ObjPtr class_loader) { + Thread* self = Thread::Current(); + ObjPtr old_dex_cache; + bool registered_with_another_class_loader = false; + { + ReaderMutexLock mu(self, *Locks::dex_lock_); + const DexCacheData* old_data = FindDexCacheDataLocked(dex_file); + old_dex_cache = DecodeDexCacheLocked(self, old_data); + if (old_dex_cache != nullptr) { + if (IsSameClassLoader(old_dex_cache, old_data, class_loader)) { + return old_dex_cache; + } else { + // TODO This is not very clean looking. Should maybe try to make a way to request exceptions + // be thrown when it's safe to do so to simplify this. + registered_with_another_class_loader = true; + } + } + } + // We need to have released the dex_lock_ to allocate safely. + if (registered_with_another_class_loader) { + ThrowDexFileAlreadyRegisteredError(self, dex_file); + return nullptr; + } + SCOPED_TRACE << __FUNCTION__ << " " << dex_file.GetLocation(); + LinearAlloc* const linear_alloc = GetOrCreateAllocatorForClassLoader(class_loader); + DCHECK(linear_alloc != nullptr); + ClassTable* table; + { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + table = InsertClassTableForClassLoader(class_loader); + } + // Don't alloc while holding the lock, since allocation may need to + // suspend all threads and another thread may need the dex_lock_ to + // get to a suspend point. + StackHandleScope<3> hs(self); + Handle h_class_loader(hs.NewHandle(class_loader)); + Handle h_dex_cache(hs.NewHandle(AllocDexCache(self, dex_file))); + { + // Avoid a deadlock between a garbage collecting thread running a checkpoint, + // a thread holding the dex lock and blocking on a condition variable regarding + // weak references access, and a thread blocking on the dex lock. + gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseClassLinker, gc::kCollectorTypeClassLinker); + WriterMutexLock mu(self, *Locks::dex_lock_); + const DexCacheData* old_data = FindDexCacheDataLocked(dex_file); + old_dex_cache = DecodeDexCacheLocked(self, old_data); + if (old_dex_cache == nullptr && h_dex_cache != nullptr) { + // Do InitializeNativeFields while holding dex lock to make sure two threads don't call it + // at the same time with the same dex cache. Since the .bss is shared this can cause failing + // DCHECK that the arrays are null. + h_dex_cache->InitializeNativeFields(&dex_file, linear_alloc); + RegisterDexFileLocked(dex_file, h_dex_cache.Get(), h_class_loader.Get()); + } + if (old_dex_cache != nullptr) { + // Another thread managed to initialize the dex cache faster, so use that DexCache. + // If this thread encountered OOME, ignore it. + DCHECK_EQ(h_dex_cache == nullptr, self->IsExceptionPending()); + self->ClearException(); + // We cannot call EnsureSameClassLoader() or allocate an exception while holding the + // dex_lock_. + if (IsSameClassLoader(old_dex_cache, old_data, h_class_loader.Get())) { + return old_dex_cache; + } else { + registered_with_another_class_loader = true; + } + } + } + if (registered_with_another_class_loader) { + ThrowDexFileAlreadyRegisteredError(self, dex_file); + return nullptr; + } + if (h_dex_cache == nullptr) { + self->AssertPendingOOMException(); + return nullptr; + } + table->InsertStrongRoot(h_dex_cache.Get()); + if (h_class_loader.Get() != nullptr) { + // Since we added a strong root to the class table, do the write barrier as required for + // remembered sets and generational GCs. + WriteBarrier::ForEveryFieldWrite(h_class_loader.Get()); + } + VLOG(class_linker) << "Registered dex file " << dex_file.GetLocation(); + PaletteNotifyDexFileLoaded(dex_file.GetLocation().c_str()); + return h_dex_cache.Get(); +} + +bool ClassLinker::IsDexFileRegistered(Thread* self, const DexFile& dex_file) { + ReaderMutexLock mu(self, *Locks::dex_lock_); + return DecodeDexCacheLocked(self, FindDexCacheDataLocked(dex_file)) != nullptr; +} + +ObjPtr ClassLinker::FindDexCache(Thread* self, const DexFile& dex_file) { + ReaderMutexLock mu(self, *Locks::dex_lock_); + const DexCacheData* dex_cache_data = FindDexCacheDataLocked(dex_file); + ObjPtr dex_cache = DecodeDexCacheLocked(self, dex_cache_data); + if (dex_cache != nullptr) { + return dex_cache; + } + // Failure, dump diagnostic and abort. + for (const DexCacheData& data : dex_caches_) { + if (DecodeDexCacheLocked(self, &data) != nullptr) { + LOG(FATAL_WITHOUT_ABORT) << "Registered dex file " << data.dex_file->GetLocation(); + } + } + LOG(FATAL) << "Failed to find DexCache for DexFile " << dex_file.GetLocation() + << " " << &dex_file << " " << dex_cache_data->dex_file; + UNREACHABLE(); +} + +ClassTable* ClassLinker::FindClassTable(Thread* self, ObjPtr dex_cache) { + const DexFile* dex_file = dex_cache->GetDexFile(); + DCHECK(dex_file != nullptr); + ReaderMutexLock mu(self, *Locks::dex_lock_); + // Search assuming unique-ness of dex file. + for (const DexCacheData& data : dex_caches_) { + // Avoid decoding (and read barriers) other unrelated dex caches. + if (data.dex_file == dex_file) { + ObjPtr registered_dex_cache = DecodeDexCacheLocked(self, &data); + if (registered_dex_cache != nullptr) { + CHECK_EQ(registered_dex_cache, dex_cache) << dex_file->GetLocation(); + return data.class_table; + } + } + } + return nullptr; +} + +const ClassLinker::DexCacheData* ClassLinker::FindDexCacheDataLocked(const DexFile& dex_file) { + // Search assuming unique-ness of dex file. + for (const DexCacheData& data : dex_caches_) { + // Avoid decoding (and read barriers) other unrelated dex caches. + if (data.dex_file == &dex_file) { + return &data; + } + } + return nullptr; +} + +void ClassLinker::CreatePrimitiveClass(Thread* self, + Primitive::Type type, + ClassRoot primitive_root) { + ObjPtr primitive_class = + AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_)); + CHECK(primitive_class != nullptr) << "OOM for primitive class " << type; + // Do not hold lock on the primitive class object, the initialization of + // primitive classes is done while the process is still single threaded. + primitive_class->SetAccessFlagsDuringLinking( + kAccPublic | kAccFinal | kAccAbstract | kAccVerificationAttempted); + primitive_class->SetPrimitiveType(type); + primitive_class->SetIfTable(GetClassRoot(this)->GetIfTable()); + // Skip EnsureSkipAccessChecksMethods(). We can skip the verified status, + // the kAccVerificationAttempted flag was added above, and there are no + // methods that need the kAccSkipAccessChecks flag. + DCHECK_EQ(primitive_class->NumMethods(), 0u); + // Primitive classes are initialized during single threaded startup, so visibly initialized. + primitive_class->SetStatusForPrimitiveOrArray(ClassStatus::kVisiblyInitialized); + const char* descriptor = Primitive::Descriptor(type); + ObjPtr existing = InsertClass(descriptor, + primitive_class, + ComputeModifiedUtf8Hash(descriptor)); + CHECK(existing == nullptr) << "InitPrimitiveClass(" << type << ") failed"; + SetClassRoot(primitive_root, primitive_class); +} + +inline ObjPtr ClassLinker::GetArrayIfTable() { + return GetClassRoot>(this)->GetIfTable(); +} + +// Create an array class (i.e. the class object for the array, not the +// array itself). "descriptor" looks like "[C" or "[[[[B" or +// "[Ljava/lang/String;". +// +// If "descriptor" refers to an array of primitives, look up the +// primitive type's internally-generated class object. +// +// "class_loader" is the class loader of the class that's referring to +// us. It's used to ensure that we're looking for the element type in +// the right context. It does NOT become the class loader for the +// array class; that always comes from the base element class. +// +// Returns null with an exception raised on failure. +ObjPtr ClassLinker::CreateArrayClass(Thread* self, + const char* descriptor, + size_t hash, + Handle class_loader) { + // Identify the underlying component type + CHECK_EQ('[', descriptor[0]); + StackHandleScope<2> hs(self); + + // This is to prevent the calls to ClassLoad and ClassPrepare which can cause java/user-supplied + // code to be executed. We put it up here so we can avoid all the allocations associated with + // creating the class. This can happen with (eg) jit threads. + if (!self->CanLoadClasses()) { + // Make sure we don't try to load anything, potentially causing an infinite loop. + ObjPtr pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return nullptr; + } + + MutableHandle component_type(hs.NewHandle(FindClass(self, descriptor + 1, + class_loader))); + if (component_type == nullptr) { + DCHECK(self->IsExceptionPending()); + // We need to accept erroneous classes as component types. + const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1); + component_type.Assign(LookupClass(self, descriptor + 1, component_hash, class_loader.Get())); + if (component_type == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } else { + self->ClearException(); + } + } + if (UNLIKELY(component_type->IsPrimitiveVoid())) { + ThrowNoClassDefFoundError("Attempt to create array of void primitive type"); + return nullptr; + } + // See if the component type is already loaded. Array classes are + // always associated with the class loader of their underlying + // element type -- an array of Strings goes with the loader for + // java/lang/String -- so we need to look for it there. (The + // caller should have checked for the existence of the class + // before calling here, but they did so with *their* class loader, + // not the component type's loader.) + // + // If we find it, the caller adds "loader" to the class' initiating + // loader list, which should prevent us from going through this again. + // + // This call is unnecessary if "loader" and "component_type->GetClassLoader()" + // are the same, because our caller (FindClass) just did the + // lookup. (Even if we get this wrong we still have correct behavior, + // because we effectively do this lookup again when we add the new + // class to the hash table --- necessary because of possible races with + // other threads.) + if (class_loader.Get() != component_type->GetClassLoader()) { + ObjPtr new_class = + LookupClass(self, descriptor, hash, component_type->GetClassLoader()); + if (new_class != nullptr) { + return new_class; + } + } + // Core array classes, i.e. Object[], Class[], String[] and primitive + // arrays, have special initialization and they should be found above. + DCHECK(!component_type->IsObjectClass() || + // Guard from false positives for errors before setting superclass. + component_type->IsErroneousUnresolved()); + DCHECK(!component_type->IsStringClass()); + DCHECK(!component_type->IsClassClass()); + DCHECK(!component_type->IsPrimitive()); + + // Fill out the fields in the Class. + // + // It is possible to execute some methods against arrays, because + // all arrays are subclasses of java_lang_Object_, so we need to set + // up a vtable. We can just point at the one in java_lang_Object_. + // + // Array classes are simple enough that we don't need to do a full + // link step. + size_t array_class_size = mirror::Array::ClassSize(image_pointer_size_); + auto visitor = [this, array_class_size, component_type](ObjPtr obj, + size_t usable_size) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedAssertNoNewTransactionRecords sanntr("CreateArrayClass"); + mirror::Class::InitializeClassVisitor init_class(array_class_size); + init_class(obj, usable_size); + ObjPtr klass = ObjPtr::DownCast(obj); + klass->SetComponentType(component_type.Get()); + // Do not hold lock for initialization, the fence issued after the visitor + // returns ensures memory visibility together with the implicit consume + // semantics (for all supported architectures) for any thread that loads + // the array class reference from any memory locations afterwards. + FinishArrayClassSetup(klass); + }; + auto new_class = hs.NewHandle( + AllocClass(self, GetClassRoot(this), array_class_size, visitor)); + if (new_class == nullptr) { + self->AssertPendingOOMException(); + return nullptr; + } + + ObjPtr existing = InsertClass(descriptor, new_class.Get(), hash); + if (existing == nullptr) { + // We postpone ClassLoad and ClassPrepare events to this point in time to avoid + // duplicate events in case of races. Array classes don't really follow dedicated + // load and prepare, anyways. + Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(new_class); + Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(new_class, new_class); + + jit::Jit::NewTypeLoadedIfUsingJit(new_class.Get()); + return new_class.Get(); + } + // Another thread must have loaded the class after we + // started but before we finished. Abandon what we've + // done. + // + // (Yes, this happens.) + + return existing; +} + +ObjPtr ClassLinker::LookupPrimitiveClass(char type) { + ClassRoot class_root; + switch (type) { + case 'B': class_root = ClassRoot::kPrimitiveByte; break; + case 'C': class_root = ClassRoot::kPrimitiveChar; break; + case 'D': class_root = ClassRoot::kPrimitiveDouble; break; + case 'F': class_root = ClassRoot::kPrimitiveFloat; break; + case 'I': class_root = ClassRoot::kPrimitiveInt; break; + case 'J': class_root = ClassRoot::kPrimitiveLong; break; + case 'S': class_root = ClassRoot::kPrimitiveShort; break; + case 'Z': class_root = ClassRoot::kPrimitiveBoolean; break; + case 'V': class_root = ClassRoot::kPrimitiveVoid; break; + default: + return nullptr; + } + return GetClassRoot(class_root, this); +} + +ObjPtr ClassLinker::FindPrimitiveClass(char type) { + ObjPtr result = LookupPrimitiveClass(type); + if (UNLIKELY(result == nullptr)) { + std::string printable_type(PrintableChar(type)); + ThrowNoClassDefFoundError("Not a primitive type: %s", printable_type.c_str()); + } + return result; +} + +ObjPtr ClassLinker::InsertClass(const char* descriptor, + ObjPtr klass, + size_t hash) { + DCHECK(Thread::Current()->CanLoadClasses()); + if (VLOG_IS_ON(class_linker)) { + ObjPtr dex_cache = klass->GetDexCache(); + std::string source; + if (dex_cache != nullptr) { + source += " from "; + source += dex_cache->GetLocation()->ToModifiedUtf8(); + } + LOG(INFO) << "Loaded class " << descriptor << source; + } + { + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + const ObjPtr class_loader = klass->GetClassLoader(); + ClassTable* const class_table = InsertClassTableForClassLoader(class_loader); + ObjPtr existing = class_table->Lookup(descriptor, hash); + if (existing != nullptr) { + return existing; + } + VerifyObject(klass); + class_table->InsertWithHash(klass, hash); + if (class_loader != nullptr) { + // This is necessary because we need to have the card dirtied for remembered sets. + WriteBarrier::ForEveryFieldWrite(class_loader); + } + if (log_new_roots_) { + new_class_roots_.push_back(GcRoot(klass)); + } + } + if (kIsDebugBuild) { + // Test that copied methods correctly can find their holder. + for (ArtMethod& method : klass->GetCopiedMethods(image_pointer_size_)) { + CHECK_EQ(GetHoldingClassOfCopiedMethod(&method), klass); + } + } + return nullptr; +} + +void ClassLinker::WriteBarrierForBootOatFileBssRoots(const OatFile* oat_file) { + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + DCHECK(!oat_file->GetBssGcRoots().empty()) << oat_file->GetLocation(); + if (log_new_roots_ && !ContainsElement(new_bss_roots_boot_oat_files_, oat_file)) { + new_bss_roots_boot_oat_files_.push_back(oat_file); + } +} + +// TODO This should really be in mirror::Class. +void ClassLinker::UpdateClassMethods(ObjPtr klass, + LengthPrefixedArray* new_methods) { + klass->SetMethodsPtrUnchecked(new_methods, + klass->NumDirectMethods(), + klass->NumDeclaredVirtualMethods()); + // Need to mark the card so that the remembered sets and mod union tables get updated. + WriteBarrier::ForEveryFieldWrite(klass); +} + +ObjPtr ClassLinker::LookupClass(Thread* self, + const char* descriptor, + ObjPtr class_loader) { + return LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor), class_loader); +} + +ObjPtr ClassLinker::LookupClass(Thread* self, + const char* descriptor, + size_t hash, + ObjPtr class_loader) { + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* const class_table = ClassTableForClassLoader(class_loader); + if (class_table != nullptr) { + ObjPtr result = class_table->Lookup(descriptor, hash); + if (result != nullptr) { + return result; + } + } + return nullptr; +} + +class MoveClassTableToPreZygoteVisitor : public ClassLoaderVisitor { + public: + MoveClassTableToPreZygoteVisitor() {} + + void Visit(ObjPtr class_loader) + REQUIRES(Locks::classlinker_classes_lock_) + REQUIRES_SHARED(Locks::mutator_lock_) override { + ClassTable* const class_table = class_loader->GetClassTable(); + if (class_table != nullptr) { + class_table->FreezeSnapshot(); + } + } +}; + +void ClassLinker::MoveClassTableToPreZygote() { + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + boot_class_table_->FreezeSnapshot(); + MoveClassTableToPreZygoteVisitor visitor; + VisitClassLoaders(&visitor); +} + +// Look up classes by hash and descriptor and put all matching ones in the result array. +class LookupClassesVisitor : public ClassLoaderVisitor { + public: + LookupClassesVisitor(const char* descriptor, + size_t hash, + std::vector>* result) + : descriptor_(descriptor), + hash_(hash), + result_(result) {} + + void Visit(ObjPtr class_loader) + REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) override { + ClassTable* const class_table = class_loader->GetClassTable(); + ObjPtr klass = class_table->Lookup(descriptor_, hash_); + // Add `klass` only if `class_loader` is its defining (not just initiating) class loader. + if (klass != nullptr && klass->GetClassLoader() == class_loader) { + result_->push_back(klass); + } + } + + private: + const char* const descriptor_; + const size_t hash_; + std::vector>* const result_; +}; + +void ClassLinker::LookupClasses(const char* descriptor, + std::vector>& result) { + result.clear(); + Thread* const self = Thread::Current(); + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + const size_t hash = ComputeModifiedUtf8Hash(descriptor); + ObjPtr klass = boot_class_table_->Lookup(descriptor, hash); + if (klass != nullptr) { + DCHECK(klass->GetClassLoader() == nullptr); + result.push_back(klass); + } + LookupClassesVisitor visitor(descriptor, hash, &result); + VisitClassLoaders(&visitor); +} + +bool ClassLinker::AttemptSupertypeVerification(Thread* self, + verifier::VerifierDeps* verifier_deps, + Handle klass, + Handle supertype) { + DCHECK(self != nullptr); + DCHECK(klass != nullptr); + DCHECK(supertype != nullptr); + + if (!supertype->IsVerified() && !supertype->IsErroneous()) { + VerifyClass(self, verifier_deps, supertype); + } + + if (supertype->IsVerified() + || supertype->ShouldVerifyAtRuntime() + || supertype->IsVerifiedNeedsAccessChecks()) { + // The supertype is either verified, or we soft failed at AOT time. + DCHECK(supertype->IsVerified() || Runtime::Current()->IsAotCompiler()); + return true; + } + // If we got this far then we have a hard failure. + std::string error_msg = + StringPrintf("Rejecting class %s that attempts to sub-type erroneous class %s", + klass->PrettyDescriptor().c_str(), + supertype->PrettyDescriptor().c_str()); + LOG(WARNING) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); + StackHandleScope<1> hs(self); + Handle cause(hs.NewHandle(self->GetException())); + if (cause != nullptr) { + // Set during VerifyClass call (if at all). + self->ClearException(); + } + // Change into a verify error. + ThrowVerifyError(klass.Get(), "%s", error_msg.c_str()); + if (cause != nullptr) { + self->GetException()->SetCause(cause.Get()); + } + ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex()); + if (Runtime::Current()->IsAotCompiler()) { + Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref); + } + // Need to grab the lock to change status. + ObjectLock super_lock(self, klass); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); + return false; +} + +verifier::FailureKind ClassLinker::VerifyClass(Thread* self, + verifier::VerifierDeps* verifier_deps, + Handle klass, + verifier::HardFailLogMode log_level) { + { + // TODO: assert that the monitor on the Class is held + ObjectLock lock(self, klass); + + // Is somebody verifying this now? + ClassStatus old_status = klass->GetStatus(); + while (old_status == ClassStatus::kVerifying) { + lock.WaitIgnoringInterrupts(); + // WaitIgnoringInterrupts can still receive an interrupt and return early, in this + // case we may see the same status again. b/62912904. This is why the check is + // greater or equal. + CHECK(klass->IsErroneous() || (klass->GetStatus() >= old_status)) + << "Class '" << klass->PrettyClass() + << "' performed an illegal verification state transition from " << old_status + << " to " << klass->GetStatus(); + old_status = klass->GetStatus(); + } + + // The class might already be erroneous, for example at compile time if we attempted to verify + // this class as a parent to another. + if (klass->IsErroneous()) { + ThrowEarlierClassFailure(klass.Get()); + return verifier::FailureKind::kHardFailure; + } + + // Don't attempt to re-verify if already verified. + if (klass->IsVerified()) { + EnsureSkipAccessChecksMethods(klass, image_pointer_size_); + if (verifier_deps != nullptr && + verifier_deps->ContainsDexFile(klass->GetDexFile()) && + !verifier_deps->HasRecordedVerifiedStatus(klass->GetDexFile(), *klass->GetClassDef()) && + !Runtime::Current()->IsAotCompiler()) { + // If the klass is verified, but `verifier_deps` did not record it, this + // means we are running background verification of a secondary dex file. + // Re-run the verifier to populate `verifier_deps`. + // No need to run the verification when running on the AOT Compiler, as + // the driver handles those multithreaded cases already. + std::string error_msg; + verifier::FailureKind failure = + PerformClassVerification(self, verifier_deps, klass, log_level, &error_msg); + // We could have soft failures, so just check that we don't have a hard + // failure. + DCHECK_NE(failure, verifier::FailureKind::kHardFailure) << error_msg; + } + return verifier::FailureKind::kNoFailure; + } + + if (klass->IsVerifiedNeedsAccessChecks()) { + if (!Runtime::Current()->IsAotCompiler()) { + // Mark the class as having a verification attempt to avoid re-running + // the verifier and avoid calling EnsureSkipAccessChecksMethods. + klass->SetVerificationAttempted(); + mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); + } + return verifier::FailureKind::kAccessChecksFailure; + } + + // For AOT, don't attempt to re-verify if we have already found we should + // verify at runtime. + if (klass->ShouldVerifyAtRuntime()) { + CHECK(Runtime::Current()->IsAotCompiler()); + return verifier::FailureKind::kSoftFailure; + } + + DCHECK_EQ(klass->GetStatus(), ClassStatus::kResolved); + mirror::Class::SetStatus(klass, ClassStatus::kVerifying, self); + + // Skip verification if disabled. + if (!Runtime::Current()->IsVerificationEnabled()) { + mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); + EnsureSkipAccessChecksMethods(klass, image_pointer_size_); + return verifier::FailureKind::kNoFailure; + } + } + + VLOG(class_linker) << "Beginning verification for class: " + << klass->PrettyDescriptor() + << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); + + // Verify super class. + StackHandleScope<2> hs(self); + MutableHandle supertype(hs.NewHandle(klass->GetSuperClass())); + // If we have a superclass and we get a hard verification failure we can return immediately. + if (supertype != nullptr && + !AttemptSupertypeVerification(self, verifier_deps, klass, supertype)) { + CHECK(self->IsExceptionPending()) << "Verification error should be pending."; + return verifier::FailureKind::kHardFailure; + } + + // Verify all default super-interfaces. + // + // (1) Don't bother if the superclass has already had a soft verification failure. + // + // (2) Interfaces shouldn't bother to do this recursive verification because they cannot cause + // recursive initialization by themselves. This is because when an interface is initialized + // directly it must not initialize its superinterfaces. We are allowed to verify regardless + // but choose not to for an optimization. If the interfaces is being verified due to a class + // initialization (which would need all the default interfaces to be verified) the class code + // will trigger the recursive verification anyway. + if ((supertype == nullptr || supertype->IsVerified()) // See (1) + && !klass->IsInterface()) { // See (2) + int32_t iftable_count = klass->GetIfTableCount(); + MutableHandle iface(hs.NewHandle(nullptr)); + // Loop through all interfaces this class has defined. It doesn't matter the order. + for (int32_t i = 0; i < iftable_count; i++) { + iface.Assign(klass->GetIfTable()->GetInterface(i)); + DCHECK(iface != nullptr); + // We only care if we have default interfaces and can skip if we are already verified... + if (LIKELY(!iface->HasDefaultMethods() || iface->IsVerified())) { + continue; + } else if (UNLIKELY(!AttemptSupertypeVerification(self, verifier_deps, klass, iface))) { + // We had a hard failure while verifying this interface. Just return immediately. + CHECK(self->IsExceptionPending()) << "Verification error should be pending."; + return verifier::FailureKind::kHardFailure; + } else if (UNLIKELY(!iface->IsVerified())) { + // We softly failed to verify the iface. Stop checking and clean up. + // Put the iface into the supertype handle so we know what caused us to fail. + supertype.Assign(iface.Get()); + break; + } + } + } + + // At this point if verification failed, then supertype is the "first" supertype that failed + // verification (without a specific order). If verification succeeded, then supertype is either + // null or the original superclass of klass and is verified. + DCHECK(supertype == nullptr || + supertype.Get() == klass->GetSuperClass() || + !supertype->IsVerified()); + + // Try to use verification information from the oat file, otherwise do runtime verification. + const DexFile& dex_file = *klass->GetDexCache()->GetDexFile(); + ClassStatus oat_file_class_status(ClassStatus::kNotReady); + bool preverified = VerifyClassUsingOatFile(self, dex_file, klass, oat_file_class_status); + + VLOG(class_linker) << "Class preverified status for class " + << klass->PrettyDescriptor() + << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8() + << ": " + << preverified + << "( " << oat_file_class_status << ")"; + + // If the oat file says the class had an error, re-run the verifier. That way we will get a + // precise error message. To ensure a rerun, test: + // mirror::Class::IsErroneous(oat_file_class_status) => !preverified + DCHECK(!mirror::Class::IsErroneous(oat_file_class_status) || !preverified); + + std::string error_msg; + verifier::FailureKind verifier_failure = verifier::FailureKind::kNoFailure; + if (!preverified) { + verifier_failure = PerformClassVerification(self, verifier_deps, klass, log_level, &error_msg); + } + + // Verification is done, grab the lock again. + ObjectLock lock(self, klass); + + if (preverified || verifier_failure != verifier::FailureKind::kHardFailure) { + if (!preverified && verifier_failure != verifier::FailureKind::kNoFailure) { + VLOG(class_linker) << "Soft verification failure in class " + << klass->PrettyDescriptor() + << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8() + << " because: " << error_msg; + } + self->AssertNoPendingException(); + // Make sure all classes referenced by catch blocks are resolved. + ResolveClassExceptionHandlerTypes(klass); + if (verifier_failure == verifier::FailureKind::kNoFailure) { + // Even though there were no verifier failures we need to respect whether the super-class and + // super-default-interfaces were verified or requiring runtime reverification. + if (supertype == nullptr + || supertype->IsVerified() + || supertype->IsVerifiedNeedsAccessChecks()) { + mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); + } else { + CHECK(Runtime::Current()->IsAotCompiler()); + CHECK_EQ(supertype->GetStatus(), ClassStatus::kRetryVerificationAtRuntime); + mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self); + // Pretend a soft failure occurred so that we don't consider the class verified below. + verifier_failure = verifier::FailureKind::kSoftFailure; + } + } else { + CHECK(verifier_failure == verifier::FailureKind::kSoftFailure || + verifier_failure == verifier::FailureKind::kTypeChecksFailure || + verifier_failure == verifier::FailureKind::kAccessChecksFailure); + // Soft failures at compile time should be retried at runtime. Soft + // failures at runtime will be handled by slow paths in the generated + // code. Set status accordingly. + if (Runtime::Current()->IsAotCompiler()) { + if (verifier_failure == verifier::FailureKind::kSoftFailure || + verifier_failure == verifier::FailureKind::kTypeChecksFailure) { + mirror::Class::SetStatus(klass, ClassStatus::kRetryVerificationAtRuntime, self); + } else { + mirror::Class::SetStatus(klass, ClassStatus::kVerifiedNeedsAccessChecks, self); + } + } else { + mirror::Class::SetStatus(klass, ClassStatus::kVerified, self); + // As this is a fake verified status, make sure the methods are _not_ marked + // kAccSkipAccessChecks later. + klass->SetVerificationAttempted(); + } + } + } else { + VLOG(verifier) << "Verification failed on class " << klass->PrettyDescriptor() + << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8() + << " because: " << error_msg; + self->AssertNoPendingException(); + ThrowVerifyError(klass.Get(), "%s", error_msg.c_str()); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); + } + if (preverified || verifier_failure == verifier::FailureKind::kNoFailure) { + if (oat_file_class_status == ClassStatus::kVerifiedNeedsAccessChecks || + UNLIKELY(Runtime::Current()->IsVerificationSoftFail())) { + // Never skip access checks if the verification soft fail is forced. + // Mark the class as having a verification attempt to avoid re-running the verifier. + klass->SetVerificationAttempted(); + } else { + // Class is verified so we don't need to do any access check on its methods. + // Let the interpreter know it by setting the kAccSkipAccessChecks flag onto each + // method. + // Note: we're going here during compilation and at runtime. When we set the + // kAccSkipAccessChecks flag when compiling image classes, the flag is recorded + // in the image and is set when loading the image. + EnsureSkipAccessChecksMethods(klass, image_pointer_size_); + } + } + // Done verifying. Notify the compiler about the verification status, in case the class + // was verified implicitly (eg super class of a compiled class). + if (Runtime::Current()->IsAotCompiler()) { + Runtime::Current()->GetCompilerCallbacks()->UpdateClassState( + ClassReference(&klass->GetDexFile(), klass->GetDexClassDefIndex()), klass->GetStatus()); + } + return verifier_failure; +} + +verifier::FailureKind ClassLinker::PerformClassVerification(Thread* self, + verifier::VerifierDeps* verifier_deps, + Handle klass, + verifier::HardFailLogMode log_level, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + return verifier::ClassVerifier::VerifyClass(self, + verifier_deps, + klass.Get(), + runtime->GetCompilerCallbacks(), + runtime->IsAotCompiler(), + log_level, + Runtime::Current()->GetTargetSdkVersion(), + error_msg); +} + +bool ClassLinker::VerifyClassUsingOatFile(Thread* self, + const DexFile& dex_file, + Handle klass, + ClassStatus& oat_file_class_status) { + // If we're compiling, we can only verify the class using the oat file if + // we are not compiling the image or if the class we're verifying is not part of + // the compilation unit (app - dependencies). We will let the compiler callback + // tell us about the latter. + if (Runtime::Current()->IsAotCompiler()) { + CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks(); + // We are compiling an app (not the image). + if (!callbacks->CanUseOatStatusForVerification(klass.Get())) { + return false; + } + } + + const OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + // In case we run without an image there won't be a backing oat file. + if (oat_dex_file == nullptr || oat_dex_file->GetOatFile() == nullptr) { + return false; + } + + uint16_t class_def_index = klass->GetDexClassDefIndex(); + oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus(); + if (oat_file_class_status >= ClassStatus::kVerified) { + return true; + } + if (oat_file_class_status >= ClassStatus::kVerifiedNeedsAccessChecks) { + // We return that the clas has already been verified, and the caller should + // check the class status to ensure we run with access checks. + return true; + } + + // Check the class status with the vdex file. + const OatFile* oat_file = oat_dex_file->GetOatFile(); + if (oat_file != nullptr) { + oat_file_class_status = oat_file->GetVdexFile()->ComputeClassStatus(self, klass); + if (oat_file_class_status >= ClassStatus::kVerifiedNeedsAccessChecks) { + return true; + } + } + + // If we only verified a subset of the classes at compile time, we can end up with classes that + // were resolved by the verifier. + if (oat_file_class_status == ClassStatus::kResolved) { + return false; + } + // We never expect a .oat file to have kRetryVerificationAtRuntime statuses. + CHECK_NE(oat_file_class_status, ClassStatus::kRetryVerificationAtRuntime) + << klass->PrettyClass() << " " << dex_file.GetLocation(); + + if (mirror::Class::IsErroneous(oat_file_class_status)) { + // Compile time verification failed with a hard error. This is caused by invalid instructions + // in the class. These errors are unrecoverable. + return false; + } + if (oat_file_class_status == ClassStatus::kNotReady) { + // Status is uninitialized if we couldn't determine the status at compile time, for example, + // not loading the class. + // TODO: when the verifier doesn't rely on Class-es failing to resolve/load the type hierarchy + // isn't a problem and this case shouldn't occur + return false; + } + std::string temp; + LOG(FATAL) << "Unexpected class status: " << oat_file_class_status + << " " << dex_file.GetLocation() << " " << klass->PrettyClass() << " " + << klass->GetDescriptor(&temp); + UNREACHABLE(); +} + +void ClassLinker::ResolveClassExceptionHandlerTypes(Handle klass) { + for (ArtMethod& method : klass->GetMethods(image_pointer_size_)) { + ResolveMethodExceptionHandlerTypes(&method); + } +} + +void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) { + // similar to DexVerifier::ScanTryCatchBlocks and dex2oat's ResolveExceptionsForMethod. + CodeItemDataAccessor accessor(method->DexInstructionData()); + if (!accessor.HasCodeItem()) { + return; // native or abstract method + } + if (accessor.TriesSize() == 0) { + return; // nothing to process + } + const uint8_t* handlers_ptr = accessor.GetCatchHandlerData(0); + uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); + for (uint32_t idx = 0; idx < handlers_size; idx++) { + CatchHandlerIterator iterator(handlers_ptr); + for (; iterator.HasNext(); iterator.Next()) { + // Ensure exception types are resolved so that they don't need resolution to be delivered, + // unresolved exception types will be ignored by exception delivery + if (iterator.GetHandlerTypeIndex().IsValid()) { + ObjPtr exception_type = ResolveType(iterator.GetHandlerTypeIndex(), method); + if (exception_type == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + Thread::Current()->ClearException(); + } + } + } + handlers_ptr = iterator.EndDataPointer(); + } +} + +ObjPtr ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, + jstring name, + jobjectArray interfaces, + jobject loader, + jobjectArray methods, + jobjectArray throws) { + Thread* self = soa.Self(); + + // This is to prevent the calls to ClassLoad and ClassPrepare which can cause java/user-supplied + // code to be executed. We put it up here so we can avoid all the allocations associated with + // creating the class. This can happen with (eg) jit-threads. + if (!self->CanLoadClasses()) { + // Make sure we don't try to load anything, potentially causing an infinite loop. + ObjPtr pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return nullptr; + } + + StackHandleScope<12> hs(self); + MutableHandle temp_klass(hs.NewHandle( + AllocClass(self, GetClassRoot(this), sizeof(mirror::Class)))); + if (temp_klass == nullptr) { + CHECK(self->IsExceptionPending()); // OOME. + return nullptr; + } + DCHECK(temp_klass->GetClass() != nullptr); + temp_klass->SetObjectSize(sizeof(mirror::Proxy)); + // Set the class access flags incl. VerificationAttempted, so we do not try to set the flag on + // the methods. + temp_klass->SetAccessFlagsDuringLinking( + kAccClassIsProxy | kAccPublic | kAccFinal | kAccVerificationAttempted); + temp_klass->SetClassLoader(soa.Decode(loader)); + DCHECK_EQ(temp_klass->GetPrimitiveType(), Primitive::kPrimNot); + temp_klass->SetName(soa.Decode(name)); + temp_klass->SetDexCache(GetClassRoot(this)->GetDexCache()); + // Object has an empty iftable, copy it for that reason. + temp_klass->SetIfTable(GetClassRoot(this)->GetIfTable()); + mirror::Class::SetStatus(temp_klass, ClassStatus::kIdx, self); + std::string storage; + const char* descriptor = temp_klass->GetDescriptor(&storage); + const size_t hash = ComputeModifiedUtf8Hash(descriptor); + + // Needs to be before we insert the class so that the allocator field is set. + LinearAlloc* const allocator = GetOrCreateAllocatorForClassLoader(temp_klass->GetClassLoader()); + + // Insert the class before loading the fields as the field roots + // (ArtField::declaring_class_) are only visited from the class + // table. There can't be any suspend points between inserting the + // class and setting the field arrays below. + ObjPtr existing = InsertClass(descriptor, temp_klass.Get(), hash); + CHECK(existing == nullptr); + + // Instance fields are inherited, but we add a couple of static fields... + const size_t num_fields = 2; + LengthPrefixedArray* sfields = AllocArtFieldArray(self, allocator, num_fields); + temp_klass->SetSFieldsPtr(sfields); + + // 1. Create a static field 'interfaces' that holds the _declared_ interfaces implemented by + // our proxy, so Class.getInterfaces doesn't return the flattened set. + ArtField& interfaces_sfield = sfields->At(0); + interfaces_sfield.SetDexFieldIndex(0); + interfaces_sfield.SetDeclaringClass(temp_klass.Get()); + interfaces_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal); + + // 2. Create a static field 'throws' that holds exceptions thrown by our methods. + ArtField& throws_sfield = sfields->At(1); + throws_sfield.SetDexFieldIndex(1); + throws_sfield.SetDeclaringClass(temp_klass.Get()); + throws_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal); + + // Proxies have 1 direct method, the constructor + const size_t num_direct_methods = 1; + + // The array we get passed contains all methods, including private and static + // ones that aren't proxied. We need to filter those out since only interface + // methods (non-private & virtual) are actually proxied. + Handle> h_methods = + hs.NewHandle(soa.Decode>(methods)); + DCHECK_EQ(h_methods->GetClass(), GetClassRoot>()) + << mirror::Class::PrettyClass(h_methods->GetClass()); + // List of the actual virtual methods this class will have. + std::vector proxied_methods; + std::vector proxied_throws_idx; + proxied_methods.reserve(h_methods->GetLength()); + proxied_throws_idx.reserve(h_methods->GetLength()); + // Filter out to only the non-private virtual methods. + for (auto [mirror, idx] : ZipCount(h_methods.Iterate())) { + ArtMethod* m = mirror->GetArtMethod(); + if (!m->IsPrivate() && !m->IsStatic()) { + proxied_methods.push_back(m); + proxied_throws_idx.push_back(idx); + } + } + const size_t num_virtual_methods = proxied_methods.size(); + // We also need to filter out the 'throws'. The 'throws' are a Class[][] that + // contains an array of all the classes each function is declared to throw. + // This is used to wrap unexpected exceptions in a + // UndeclaredThrowableException exception. This array is in the same order as + // the methods array and like the methods array must be filtered to remove any + // non-proxied methods. + const bool has_filtered_methods = + static_cast(num_virtual_methods) != h_methods->GetLength(); + MutableHandle>> original_proxied_throws( + hs.NewHandle(soa.Decode>>(throws))); + MutableHandle>> proxied_throws( + hs.NewHandle>>( + (has_filtered_methods) + ? mirror::ObjectArray>::Alloc( + self, original_proxied_throws->GetClass(), num_virtual_methods) + : original_proxied_throws.Get())); + if (proxied_throws.IsNull() && !original_proxied_throws.IsNull()) { + self->AssertPendingOOMException(); + return nullptr; + } + if (has_filtered_methods) { + for (auto [orig_idx, new_idx] : ZipCount(MakeIterationRange(proxied_throws_idx))) { + DCHECK_LE(new_idx, orig_idx); + proxied_throws->Set(new_idx, original_proxied_throws->Get(orig_idx)); + } + } + + // Create the methods array. + LengthPrefixedArray* proxy_class_methods = AllocArtMethodArray( + self, allocator, num_direct_methods + num_virtual_methods); + // Currently AllocArtMethodArray cannot return null, but the OOM logic is left there in case we + // want to throw OOM in the future. + if (UNLIKELY(proxy_class_methods == nullptr)) { + self->AssertPendingOOMException(); + return nullptr; + } + temp_klass->SetMethodsPtr(proxy_class_methods, num_direct_methods, num_virtual_methods); + + // Create the single direct method. + CreateProxyConstructor(temp_klass, temp_klass->GetDirectMethodUnchecked(0, image_pointer_size_)); + + // Create virtual method using specified prototypes. + // TODO These should really use the iterators. + for (size_t i = 0; i < num_virtual_methods; ++i) { + auto* virtual_method = temp_klass->GetVirtualMethodUnchecked(i, image_pointer_size_); + auto* prototype = proxied_methods[i]; + CreateProxyMethod(temp_klass, prototype, virtual_method); + DCHECK(virtual_method->GetDeclaringClass() != nullptr); + DCHECK(prototype->GetDeclaringClass() != nullptr); + } + + // The super class is java.lang.reflect.Proxy + temp_klass->SetSuperClass(GetClassRoot(this)); + // Now effectively in the loaded state. + mirror::Class::SetStatus(temp_klass, ClassStatus::kLoaded, self); + self->AssertNoPendingException(); + + // At this point the class is loaded. Publish a ClassLoad event. + // Note: this may be a temporary class. It is a listener's responsibility to handle this. + Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(temp_klass); + + MutableHandle klass = hs.NewHandle(nullptr); + { + // Must hold lock on object when resolved. + ObjectLock resolution_lock(self, temp_klass); + // Link the fields and virtual methods, creating vtable and iftables. + // The new class will replace the old one in the class table. + Handle> h_interfaces( + hs.NewHandle(soa.Decode>(interfaces))); + if (!LinkClass(self, descriptor, temp_klass, h_interfaces, &klass)) { + if (!temp_klass->IsErroneous()) { + mirror::Class::SetStatus(temp_klass, ClassStatus::kErrorUnresolved, self); + } + return nullptr; + } + } + CHECK(temp_klass->IsRetired()); + CHECK_NE(temp_klass.Get(), klass.Get()); + + CHECK_EQ(interfaces_sfield.GetDeclaringClass(), klass.Get()); + interfaces_sfield.SetObject( + klass.Get(), + soa.Decode>(interfaces)); + CHECK_EQ(throws_sfield.GetDeclaringClass(), klass.Get()); + throws_sfield.SetObject( + klass.Get(), + proxied_throws.Get()); + + Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(temp_klass, klass); + + // SubtypeCheckInfo::Initialized must happen-before any new-instance for that type. + // See also ClassLinker::EnsureInitialized(). + if (kBitstringSubtypeCheckEnabled) { + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheck>::EnsureInitialized(klass.Get()); + // TODO: Avoid taking subtype_check_lock_ if SubtypeCheck for j.l.r.Proxy is already assigned. + } + + VisiblyInitializedCallback* callback = nullptr; + { + // Lock on klass is released. Lock new class object. + ObjectLock initialization_lock(self, klass); + EnsureSkipAccessChecksMethods(klass, image_pointer_size_); + // Conservatively go through the ClassStatus::kInitialized state. + callback = MarkClassInitialized(self, klass); + } + if (callback != nullptr) { + callback->MakeVisible(self); + } + + // Consistency checks. + if (kIsDebugBuild) { + CHECK(klass->GetIFieldsPtr() == nullptr); + CheckProxyConstructor(klass->GetDirectMethod(0, image_pointer_size_)); + + for (size_t i = 0; i < num_virtual_methods; ++i) { + auto* virtual_method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_); + CheckProxyMethod(virtual_method, proxied_methods[i]); + } + + StackHandleScope<1> hs2(self); + Handle decoded_name = hs2.NewHandle(soa.Decode(name)); + std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces", + decoded_name->ToModifiedUtf8().c_str())); + CHECK_EQ(ArtField::PrettyField(klass->GetStaticField(0)), interfaces_field_name); + + std::string throws_field_name(StringPrintf("java.lang.Class[][] %s.throws", + decoded_name->ToModifiedUtf8().c_str())); + CHECK_EQ(ArtField::PrettyField(klass->GetStaticField(1)), throws_field_name); + + CHECK_EQ(klass.Get()->GetProxyInterfaces(), + soa.Decode>(interfaces)); + CHECK_EQ(klass.Get()->GetProxyThrows(), + proxied_throws.Get()); + } + return klass.Get(); +} + +void ClassLinker::CreateProxyConstructor(Handle klass, ArtMethod* out) { + // Create constructor for Proxy that must initialize the method. + ObjPtr proxy_class = GetClassRoot(this); + CHECK_EQ(proxy_class->NumDirectMethods(), 21u); + + // Find the (InvocationHandler)V method. The exact method offset varies depending + // on which front-end compiler was used to build the libcore DEX files. + ArtMethod* proxy_constructor = + jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Proxy_init); + DCHECK(proxy_constructor != nullptr) + << "Could not find method in java.lang.reflect.Proxy"; + + // Clone the existing constructor of Proxy (our constructor would just invoke it so steal its + // code_ too) + DCHECK(out != nullptr); + out->CopyFrom(proxy_constructor, image_pointer_size_); + // Make this constructor public and fix the class to be our Proxy version. + // Mark kAccCompileDontBother so that we don't take JIT samples for the method. b/62349349 + // Note that the compiler calls a ResolveMethod() overload that does not handle a Proxy referrer. + out->SetAccessFlags((out->GetAccessFlags() & ~kAccProtected) | + kAccPublic | + kAccCompileDontBother); + out->SetDeclaringClass(klass.Get()); + + // Set the original constructor method. + out->SetDataPtrSize(proxy_constructor, image_pointer_size_); +} + +void ClassLinker::CheckProxyConstructor(ArtMethod* constructor) const { + CHECK(constructor->IsConstructor()); + auto* np = constructor->GetInterfaceMethodIfProxy(image_pointer_size_); + CHECK_STREQ(np->GetName(), ""); + CHECK_STREQ(np->GetSignature().ToString().c_str(), "(Ljava/lang/reflect/InvocationHandler;)V"); + DCHECK(constructor->IsPublic()); +} + +void ClassLinker::CreateProxyMethod(Handle klass, ArtMethod* prototype, + ArtMethod* out) { + // We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialize + // as necessary + DCHECK(out != nullptr); + out->CopyFrom(prototype, image_pointer_size_); + + // Set class to be the concrete proxy class. + out->SetDeclaringClass(klass.Get()); + // Clear the abstract and default flags to ensure that defaults aren't picked in + // preference to the invocation handler. + const uint32_t kRemoveFlags = kAccAbstract | kAccDefault; + // Make the method final. + // Mark kAccCompileDontBother so that we don't take JIT samples for the method. b/62349349 + const uint32_t kAddFlags = kAccFinal | kAccCompileDontBother; + out->SetAccessFlags((out->GetAccessFlags() & ~kRemoveFlags) | kAddFlags); + + // Set the original interface method. + out->SetDataPtrSize(prototype, image_pointer_size_); + + // At runtime the method looks like a reference and argument saving method, clone the code + // related parameters from this method. + out->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()); +} + +void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) const { + // Basic consistency checks. + CHECK(!prototype->IsFinal()); + CHECK(method->IsFinal()); + CHECK(method->IsInvokable()); + + // The proxy method doesn't have its own dex cache or dex file and so it steals those of its + // interface prototype. The exception to this are Constructors and the Class of the Proxy itself. + CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex()); + CHECK_EQ(prototype, method->GetInterfaceMethodIfProxy(image_pointer_size_)); +} + +bool ClassLinker::CanWeInitializeClass(ObjPtr klass, bool can_init_statics, + bool can_init_parents) { + if (can_init_statics && can_init_parents) { + return true; + } + if (!can_init_statics) { + // Check if there's a class initializer. + ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_); + if (clinit != nullptr) { + return false; + } + // Check if there are encoded static values needing initialization. + if (klass->NumStaticFields() != 0) { + const dex::ClassDef* dex_class_def = klass->GetClassDef(); + DCHECK(dex_class_def != nullptr); + if (dex_class_def->static_values_off_ != 0) { + return false; + } + } + } + // If we are a class we need to initialize all interfaces with default methods when we are + // initialized. Check all of them. + if (!klass->IsInterface()) { + size_t num_interfaces = klass->GetIfTableCount(); + for (size_t i = 0; i < num_interfaces; i++) { + ObjPtr iface = klass->GetIfTable()->GetInterface(i); + if (iface->HasDefaultMethods() && !iface->IsInitialized()) { + if (!can_init_parents || !CanWeInitializeClass(iface, can_init_statics, can_init_parents)) { + return false; + } + } + } + } + if (klass->IsInterface() || !klass->HasSuperClass()) { + return true; + } + ObjPtr super_class = klass->GetSuperClass(); + if (super_class->IsInitialized()) { + return true; + } + return can_init_parents && CanWeInitializeClass(super_class, can_init_statics, can_init_parents); +} + +bool ClassLinker::InitializeClass(Thread* self, + Handle klass, + bool can_init_statics, + bool can_init_parents) { + // see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol + + // Are we already initialized and therefore done? + // Note: we differ from the JLS here as we don't do this under the lock, this is benign as + // an initialized class will never change its state. + if (klass->IsInitialized()) { + return true; + } + + // Fast fail if initialization requires a full runtime. Not part of the JLS. + if (!CanWeInitializeClass(klass.Get(), can_init_statics, can_init_parents)) { + return false; + } + + self->AllowThreadSuspension(); + Runtime* const runtime = Runtime::Current(); + const bool stats_enabled = runtime->HasStatsEnabled(); + uint64_t t0; + { + ObjectLock lock(self, klass); + + // Re-check under the lock in case another thread initialized ahead of us. + if (klass->IsInitialized()) { + return true; + } + + // Was the class already found to be erroneous? Done under the lock to match the JLS. + if (klass->IsErroneous()) { + ThrowEarlierClassFailure(klass.Get(), true, /* log= */ true); + VlogClassInitializationFailure(klass); + return false; + } + + CHECK(klass->IsResolved() && !klass->IsErroneousResolved()) + << klass->PrettyClass() << ": state=" << klass->GetStatus(); + + if (!klass->IsVerified()) { + VerifyClass(self, /*verifier_deps= */ nullptr, klass); + if (!klass->IsVerified()) { + // We failed to verify, expect either the klass to be erroneous or verification failed at + // compile time. + if (klass->IsErroneous()) { + // The class is erroneous. This may be a verifier error, or another thread attempted + // verification and/or initialization and failed. We can distinguish those cases by + // whether an exception is already pending. + if (self->IsExceptionPending()) { + // Check that it's a VerifyError. + DCHECK_EQ("java.lang.Class", + mirror::Class::PrettyClass(self->GetException()->GetClass())); + } else { + // Check that another thread attempted initialization. + DCHECK_NE(0, klass->GetClinitThreadId()); + DCHECK_NE(self->GetTid(), klass->GetClinitThreadId()); + // Need to rethrow the previous failure now. + ThrowEarlierClassFailure(klass.Get(), true); + } + VlogClassInitializationFailure(klass); + } else { + CHECK(Runtime::Current()->IsAotCompiler()); + CHECK(klass->ShouldVerifyAtRuntime() || klass->IsVerifiedNeedsAccessChecks()); + self->AssertNoPendingException(); + self->SetException(Runtime::Current()->GetPreAllocatedNoClassDefFoundError()); + } + self->AssertPendingException(); + return false; + } else { + self->AssertNoPendingException(); + } + + // A separate thread could have moved us all the way to initialized. A "simple" example + // involves a subclass of the current class being initialized at the same time (which + // will implicitly initialize the superclass, if scheduled that way). b/28254258 + DCHECK(!klass->IsErroneous()) << klass->GetStatus(); + if (klass->IsInitialized()) { + return true; + } + } + + // If the class is ClassStatus::kInitializing, either this thread is + // initializing higher up the stack or another thread has beat us + // to initializing and we need to wait. Either way, this + // invocation of InitializeClass will not be responsible for + // running and will return. + if (klass->GetStatus() == ClassStatus::kInitializing) { + // Could have got an exception during verification. + if (self->IsExceptionPending()) { + VlogClassInitializationFailure(klass); + return false; + } + // We caught somebody else in the act; was it us? + if (klass->GetClinitThreadId() == self->GetTid()) { + // Yes. That's fine. Return so we can continue initializing. + return true; + } + // No. That's fine. Wait for another thread to finish initializing. + return WaitForInitializeClass(klass, self, lock); + } + + // Try to get the oat class's status for this class if the oat file is present. The compiler + // tries to validate superclass descriptors, and writes the result into the oat file. + // Runtime correctness is guaranteed by classpath checks done on loading. If the classpath + // is different at runtime than it was at compile time, the oat file is rejected. So if the + // oat file is present, the classpaths must match, and the runtime time check can be skipped. + bool has_oat_class = false; + const OatFile::OatClass oat_class = (runtime->IsStarted() && !runtime->IsAotCompiler()) + ? OatFile::FindOatClass(klass->GetDexFile(), klass->GetDexClassDefIndex(), &has_oat_class) + : OatFile::OatClass::Invalid(); + if (oat_class.GetStatus() < ClassStatus::kSuperclassValidated && + !ValidateSuperClassDescriptors(klass)) { + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); + return false; + } + self->AllowThreadSuspension(); + + CHECK_EQ(klass->GetStatus(), ClassStatus::kVerified) << klass->PrettyClass() + << " self.tid=" << self->GetTid() << " clinit.tid=" << klass->GetClinitThreadId(); + + // From here out other threads may observe that we're initializing and so changes of state + // require the a notification. + klass->SetClinitThreadId(self->GetTid()); + mirror::Class::SetStatus(klass, ClassStatus::kInitializing, self); + + t0 = stats_enabled ? NanoTime() : 0u; + } + + uint64_t t_sub = 0; + + // Initialize super classes, must be done while initializing for the JLS. + if (!klass->IsInterface() && klass->HasSuperClass()) { + ObjPtr super_class = klass->GetSuperClass(); + if (!super_class->IsInitialized()) { + CHECK(!super_class->IsInterface()); + CHECK(can_init_parents); + StackHandleScope<1> hs(self); + Handle handle_scope_super(hs.NewHandle(super_class)); + uint64_t super_t0 = stats_enabled ? NanoTime() : 0u; + bool super_initialized = InitializeClass(self, handle_scope_super, can_init_statics, true); + uint64_t super_t1 = stats_enabled ? NanoTime() : 0u; + if (!super_initialized) { + // The super class was verified ahead of entering initializing, we should only be here if + // the super class became erroneous due to initialization. + // For the case of aot compiler, the super class might also be initializing but we don't + // want to process circular dependencies in pre-compile. + CHECK(self->IsExceptionPending()) + << "Super class initialization failed for " + << handle_scope_super->PrettyDescriptor() + << " that has unexpected status " << handle_scope_super->GetStatus() + << "\nPending exception:\n" + << (self->GetException() != nullptr ? self->GetException()->Dump() : ""); + ObjectLock lock(self, klass); + // Initialization failed because the super-class is erroneous. + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); + return false; + } + t_sub = super_t1 - super_t0; + } + } + + if (!klass->IsInterface()) { + // Initialize interfaces with default methods for the JLS. + size_t num_direct_interfaces = klass->NumDirectInterfaces(); + // Only setup the (expensive) handle scope if we actually need to. + if (UNLIKELY(num_direct_interfaces > 0)) { + StackHandleScope<1> hs_iface(self); + MutableHandle handle_scope_iface(hs_iface.NewHandle(nullptr)); + for (size_t i = 0; i < num_direct_interfaces; i++) { + handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass.Get(), i)); + CHECK(handle_scope_iface != nullptr) << klass->PrettyDescriptor() << " iface #" << i; + CHECK(handle_scope_iface->IsInterface()); + if (handle_scope_iface->HasBeenRecursivelyInitialized()) { + // We have already done this for this interface. Skip it. + continue; + } + // We cannot just call initialize class directly because we need to ensure that ALL + // interfaces with default methods are initialized. Non-default interface initialization + // will not affect other non-default super-interfaces. + // This is not very precise, misses all walking. + uint64_t inf_t0 = stats_enabled ? NanoTime() : 0u; + bool iface_initialized = InitializeDefaultInterfaceRecursive(self, + handle_scope_iface, + can_init_statics, + can_init_parents); + uint64_t inf_t1 = stats_enabled ? NanoTime() : 0u; + if (!iface_initialized) { + ObjectLock lock(self, klass); + // Initialization failed because one of our interfaces with default methods is erroneous. + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); + return false; + } + t_sub += inf_t1 - inf_t0; + } + } + } + + const size_t num_static_fields = klass->NumStaticFields(); + if (num_static_fields > 0) { + const dex::ClassDef* dex_class_def = klass->GetClassDef(); + CHECK(dex_class_def != nullptr); + StackHandleScope<3> hs(self); + Handle class_loader(hs.NewHandle(klass->GetClassLoader())); + Handle dex_cache(hs.NewHandle(klass->GetDexCache())); + + // Eagerly fill in static fields so that the we don't have to do as many expensive + // Class::FindStaticField in ResolveField. + for (size_t i = 0; i < num_static_fields; ++i) { + ArtField* field = klass->GetStaticField(i); + const uint32_t field_idx = field->GetDexFieldIndex(); + ArtField* resolved_field = dex_cache->GetResolvedField(field_idx); + if (resolved_field == nullptr) { + // Populating cache of a dex file which defines `klass` should always be allowed. + DCHECK(!hiddenapi::ShouldDenyAccessToMember( + field, + hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()), + hiddenapi::AccessMethod::kNone)); + dex_cache->SetResolvedField(field_idx, field); + } else { + DCHECK_EQ(field, resolved_field); + } + } + + annotations::RuntimeEncodedStaticFieldValueIterator value_it(dex_cache, + class_loader, + this, + *dex_class_def); + const DexFile& dex_file = *dex_cache->GetDexFile(); + + if (value_it.HasNext()) { + ClassAccessor accessor(dex_file, *dex_class_def); + CHECK(can_init_statics); + for (const ClassAccessor::Field& field : accessor.GetStaticFields()) { + if (!value_it.HasNext()) { + break; + } + ArtField* art_field = ResolveField(field.GetIndex(), + dex_cache, + class_loader, + /* is_static= */ true); + if (Runtime::Current()->IsActiveTransaction()) { + value_it.ReadValueToField(art_field); + } else { + value_it.ReadValueToField(art_field); + } + if (self->IsExceptionPending()) { + break; + } + value_it.Next(); + } + DCHECK(self->IsExceptionPending() || !value_it.HasNext()); + } + } + + + if (!self->IsExceptionPending()) { + ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_); + if (clinit != nullptr) { + CHECK(can_init_statics); + JValue result; + clinit->Invoke(self, nullptr, 0, &result, "V"); + } + } + self->AllowThreadSuspension(); + uint64_t t1 = stats_enabled ? NanoTime() : 0u; + + VisiblyInitializedCallback* callback = nullptr; + bool success = true; + { + ObjectLock lock(self, klass); + + if (self->IsExceptionPending()) { + WrapExceptionInInitializer(klass); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); + success = false; + } else if (Runtime::Current()->IsTransactionAborted()) { + // The exception thrown when the transaction aborted has been caught and cleared + // so we need to throw it again now. + VLOG(compiler) << "Return from class initializer of " + << mirror::Class::PrettyDescriptor(klass.Get()) + << " without exception while transaction was aborted: re-throw it now."; + runtime->ThrowTransactionAbortError(self); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); + success = false; + } else { + if (stats_enabled) { + RuntimeStats* global_stats = runtime->GetStats(); + RuntimeStats* thread_stats = self->GetStats(); + ++global_stats->class_init_count; + ++thread_stats->class_init_count; + global_stats->class_init_time_ns += (t1 - t0 - t_sub); + thread_stats->class_init_time_ns += (t1 - t0 - t_sub); + } + // Set the class as initialized except if failed to initialize static fields. + callback = MarkClassInitialized(self, klass); + if (VLOG_IS_ON(class_linker)) { + std::string temp; + LOG(INFO) << "Initialized class " << klass->GetDescriptor(&temp) << " from " << + klass->GetLocation(); + } + } + } + if (callback != nullptr) { + callback->MakeVisible(self); + } + return success; +} + +// We recursively run down the tree of interfaces. We need to do this in the order they are declared +// and perform the initialization only on those interfaces that contain default methods. +bool ClassLinker::InitializeDefaultInterfaceRecursive(Thread* self, + Handle iface, + bool can_init_statics, + bool can_init_parents) { + CHECK(iface->IsInterface()); + size_t num_direct_ifaces = iface->NumDirectInterfaces(); + // Only create the (expensive) handle scope if we need it. + if (UNLIKELY(num_direct_ifaces > 0)) { + StackHandleScope<1> hs(self); + MutableHandle handle_super_iface(hs.NewHandle(nullptr)); + // First we initialize all of iface's super-interfaces recursively. + for (size_t i = 0; i < num_direct_ifaces; i++) { + ObjPtr super_iface = mirror::Class::GetDirectInterface(self, iface.Get(), i); + CHECK(super_iface != nullptr) << iface->PrettyDescriptor() << " iface #" << i; + if (!super_iface->HasBeenRecursivelyInitialized()) { + // Recursive step + handle_super_iface.Assign(super_iface); + if (!InitializeDefaultInterfaceRecursive(self, + handle_super_iface, + can_init_statics, + can_init_parents)) { + return false; + } + } + } + } + + bool result = true; + // Then we initialize 'iface' if it has default methods. We do not need to (and in fact must not) + // initialize if we don't have default methods. + if (iface->HasDefaultMethods()) { + result = EnsureInitialized(self, iface, can_init_statics, can_init_parents); + } + + // Mark that this interface has undergone recursive default interface initialization so we know we + // can skip it on any later class initializations. We do this even if we are not a default + // interface since we can still avoid the traversal. This is purely a performance optimization. + if (result) { + // TODO This should be done in a better way + // Note: Use a try-lock to avoid blocking when someone else is holding the lock on this + // interface. It is bad (Java) style, but not impossible. Marking the recursive + // initialization is a performance optimization (to avoid another idempotent visit + // for other implementing classes/interfaces), and can be revisited later. + ObjectTryLock lock(self, iface); + if (lock.Acquired()) { + iface->SetRecursivelyInitialized(); + } + } + return result; +} + +bool ClassLinker::WaitForInitializeClass(Handle klass, + Thread* self, + ObjectLock& lock) + REQUIRES_SHARED(Locks::mutator_lock_) { + while (true) { + self->AssertNoPendingException(); + CHECK(!klass->IsInitialized()); + lock.WaitIgnoringInterrupts(); + + // When we wake up, repeat the test for init-in-progress. If + // there's an exception pending (only possible if + // we were not using WaitIgnoringInterrupts), bail out. + if (self->IsExceptionPending()) { + WrapExceptionInInitializer(klass); + mirror::Class::SetStatus(klass, ClassStatus::kErrorResolved, self); + return false; + } + // Spurious wakeup? Go back to waiting. + if (klass->GetStatus() == ClassStatus::kInitializing) { + continue; + } + if (klass->GetStatus() == ClassStatus::kVerified && + Runtime::Current()->IsAotCompiler()) { + // Compile time initialization failed. + return false; + } + if (klass->IsErroneous()) { + // The caller wants an exception, but it was thrown in a + // different thread. Synthesize one here. + ThrowNoClassDefFoundError(" failed for class %s; see exception in other thread", + klass->PrettyDescriptor().c_str()); + VlogClassInitializationFailure(klass); + return false; + } + if (klass->IsInitialized()) { + return true; + } + LOG(FATAL) << "Unexpected class status. " << klass->PrettyClass() << " is " + << klass->GetStatus(); + } + UNREACHABLE(); +} + +static void ThrowSignatureCheckResolveReturnTypeException(Handle klass, + Handle super_klass, + ArtMethod* method, + ArtMethod* m) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(Thread::Current()->IsExceptionPending()); + DCHECK(!m->IsProxyMethod()); + const DexFile* dex_file = m->GetDexFile(); + const dex::MethodId& method_id = dex_file->GetMethodId(m->GetDexMethodIndex()); + const dex::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); + dex::TypeIndex return_type_idx = proto_id.return_type_idx_; + std::string return_type = dex_file->PrettyType(return_type_idx); + std::string class_loader = mirror::Object::PrettyTypeOf(m->GetDeclaringClass()->GetClassLoader()); + ThrowWrappedLinkageError(klass.Get(), + "While checking class %s method %s signature against %s %s: " + "Failed to resolve return type %s with %s", + mirror::Class::PrettyDescriptor(klass.Get()).c_str(), + ArtMethod::PrettyMethod(method).c_str(), + super_klass->IsInterface() ? "interface" : "superclass", + mirror::Class::PrettyDescriptor(super_klass.Get()).c_str(), + return_type.c_str(), class_loader.c_str()); +} + +static void ThrowSignatureCheckResolveArgException(Handle klass, + Handle super_klass, + ArtMethod* method, + ArtMethod* m, + uint32_t index, + dex::TypeIndex arg_type_idx) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(Thread::Current()->IsExceptionPending()); + DCHECK(!m->IsProxyMethod()); + const DexFile* dex_file = m->GetDexFile(); + std::string arg_type = dex_file->PrettyType(arg_type_idx); + std::string class_loader = mirror::Object::PrettyTypeOf(m->GetDeclaringClass()->GetClassLoader()); + ThrowWrappedLinkageError(klass.Get(), + "While checking class %s method %s signature against %s %s: " + "Failed to resolve arg %u type %s with %s", + mirror::Class::PrettyDescriptor(klass.Get()).c_str(), + ArtMethod::PrettyMethod(method).c_str(), + super_klass->IsInterface() ? "interface" : "superclass", + mirror::Class::PrettyDescriptor(super_klass.Get()).c_str(), + index, arg_type.c_str(), class_loader.c_str()); +} + +static void ThrowSignatureMismatch(Handle klass, + Handle super_klass, + ArtMethod* method, + const std::string& error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + ThrowLinkageError(klass.Get(), + "Class %s method %s resolves differently in %s %s: %s", + mirror::Class::PrettyDescriptor(klass.Get()).c_str(), + ArtMethod::PrettyMethod(method).c_str(), + super_klass->IsInterface() ? "interface" : "superclass", + mirror::Class::PrettyDescriptor(super_klass.Get()).c_str(), + error_msg.c_str()); +} + +static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, + Handle klass, + Handle super_klass, + ArtMethod* method1, + ArtMethod* method2) + REQUIRES_SHARED(Locks::mutator_lock_) { + { + StackHandleScope<1> hs(self); + Handle return_type(hs.NewHandle(method1->ResolveReturnType())); + if (UNLIKELY(return_type == nullptr)) { + ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1); + return false; + } + ObjPtr other_return_type = method2->ResolveReturnType(); + if (UNLIKELY(other_return_type == nullptr)) { + ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2); + return false; + } + if (UNLIKELY(other_return_type != return_type.Get())) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Return types mismatch: %s(%p) vs %s(%p)", + return_type->PrettyClassAndClassLoader().c_str(), + return_type.Get(), + other_return_type->PrettyClassAndClassLoader().c_str(), + other_return_type.Ptr())); + return false; + } + } + const dex::TypeList* types1 = method1->GetParameterTypeList(); + const dex::TypeList* types2 = method2->GetParameterTypeList(); + if (types1 == nullptr) { + if (types2 != nullptr && types2->Size() != 0) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Type list mismatch with %s", + method2->PrettyMethod(true).c_str())); + return false; + } + return true; + } else if (UNLIKELY(types2 == nullptr)) { + if (types1->Size() != 0) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Type list mismatch with %s", + method2->PrettyMethod(true).c_str())); + return false; + } + return true; + } + uint32_t num_types = types1->Size(); + if (UNLIKELY(num_types != types2->Size())) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Type list mismatch with %s", + method2->PrettyMethod(true).c_str())); + return false; + } + for (uint32_t i = 0; i < num_types; ++i) { + StackHandleScope<1> hs(self); + dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_; + Handle param_type(hs.NewHandle( + method1->ResolveClassFromTypeIndex(param_type_idx))); + if (UNLIKELY(param_type == nullptr)) { + ThrowSignatureCheckResolveArgException(klass, super_klass, method1, + method1, i, param_type_idx); + return false; + } + dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_; + ObjPtr other_param_type = + method2->ResolveClassFromTypeIndex(other_param_type_idx); + if (UNLIKELY(other_param_type == nullptr)) { + ThrowSignatureCheckResolveArgException(klass, super_klass, method1, + method2, i, other_param_type_idx); + return false; + } + if (UNLIKELY(param_type.Get() != other_param_type)) { + ThrowSignatureMismatch(klass, super_klass, method1, + StringPrintf("Parameter %u type mismatch: %s(%p) vs %s(%p)", + i, + param_type->PrettyClassAndClassLoader().c_str(), + param_type.Get(), + other_param_type->PrettyClassAndClassLoader().c_str(), + other_param_type.Ptr())); + return false; + } + } + return true; +} + + +bool ClassLinker::ValidateSuperClassDescriptors(Handle klass) { + if (klass->IsInterface()) { + return true; + } + // Begin with the methods local to the superclass. + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + MutableHandle super_klass(hs.NewHandle(nullptr)); + if (klass->HasSuperClass() && + klass->GetClassLoader() != klass->GetSuperClass()->GetClassLoader()) { + super_klass.Assign(klass->GetSuperClass()); + for (int i = klass->GetSuperClass()->GetVTableLength() - 1; i >= 0; --i) { + auto* m = klass->GetVTableEntry(i, image_pointer_size_); + auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_); + if (m != super_m) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, + klass, + super_klass, + m, + super_m))) { + self->AssertPendingException(); + return false; + } + } + } + } + for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { + super_klass.Assign(klass->GetIfTable()->GetInterface(i)); + if (klass->GetClassLoader() != super_klass->GetClassLoader()) { + uint32_t num_methods = super_klass->NumVirtualMethods(); + for (uint32_t j = 0; j < num_methods; ++j) { + auto* m = klass->GetIfTable()->GetMethodArray(i)->GetElementPtrSize( + j, image_pointer_size_); + auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_); + if (m != super_m) { + if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, + klass, + super_klass, + m, + super_m))) { + self->AssertPendingException(); + return false; + } + } + } + } + } + return true; +} + +bool ClassLinker::EnsureInitialized(Thread* self, + Handle c, + bool can_init_fields, + bool can_init_parents) { + DCHECK(c != nullptr); + + if (c->IsInitialized()) { + // If we've seen an initialized but not visibly initialized class + // many times, request visible initialization. + if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) { + // Thanks to the x86 memory model classes skip the initialized status. + DCHECK(c->IsVisiblyInitialized()); + } else if (UNLIKELY(!c->IsVisiblyInitialized())) { + if (self->IncrementMakeVisiblyInitializedCounter()) { + MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ false); + } + } + DCHECK(c->WasVerificationAttempted()) << c->PrettyClassAndClassLoader(); + return true; + } + // SubtypeCheckInfo::Initialized must happen-before any new-instance for that type. + // + // Ensure the bitstring is initialized before any of the class initialization + // logic occurs. Once a class initializer starts running, objects can + // escape into the heap and use the subtype checking code. + // + // Note: A class whose SubtypeCheckInfo is at least Initialized means it + // can be used as a source for the IsSubClass check, and that all ancestors + // of the class are Assigned (can be used as a target for IsSubClass check) + // or Overflowed (can be used as a source for IsSubClass check). + if (kBitstringSubtypeCheckEnabled) { + MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_); + SubtypeCheck>::EnsureInitialized(c.Get()); + // TODO: Avoid taking subtype_check_lock_ if SubtypeCheck is already initialized. + } + const bool success = InitializeClass(self, c, can_init_fields, can_init_parents); + if (!success) { + if (can_init_fields && can_init_parents) { + CHECK(self->IsExceptionPending()) << c->PrettyClass(); + } else { + // There may or may not be an exception pending. If there is, clear it. + // We propagate the exception only if we can initialize fields and parents. + self->ClearException(); + } + } else { + self->AssertNoPendingException(); + } + return success; +} + +void ClassLinker::FixupTemporaryDeclaringClass(ObjPtr temp_class, + ObjPtr new_class) { + DCHECK_EQ(temp_class->NumInstanceFields(), 0u); + for (ArtField& field : new_class->GetIFields()) { + if (field.GetDeclaringClass() == temp_class) { + field.SetDeclaringClass(new_class); + } + } + + DCHECK_EQ(temp_class->NumStaticFields(), 0u); + for (ArtField& field : new_class->GetSFields()) { + if (field.GetDeclaringClass() == temp_class) { + field.SetDeclaringClass(new_class); + } + } + + DCHECK_EQ(temp_class->NumDirectMethods(), 0u); + DCHECK_EQ(temp_class->NumVirtualMethods(), 0u); + for (auto& method : new_class->GetMethods(image_pointer_size_)) { + if (method.GetDeclaringClass() == temp_class) { + method.SetDeclaringClass(new_class); + } + } + + // Make sure the remembered set and mod-union tables know that we updated some of the native + // roots. + WriteBarrier::ForEveryFieldWrite(new_class); +} + +void ClassLinker::RegisterClassLoader(ObjPtr class_loader) { + CHECK(class_loader->GetAllocator() == nullptr); + CHECK(class_loader->GetClassTable() == nullptr); + Thread* const self = Thread::Current(); + ClassLoaderData data; + data.weak_root = self->GetJniEnv()->GetVm()->AddWeakGlobalRef(self, class_loader); + // Create and set the class table. + data.class_table = new ClassTable; + class_loader->SetClassTable(data.class_table); + // Create and set the linear allocator. + data.allocator = Runtime::Current()->CreateLinearAlloc(); + class_loader->SetAllocator(data.allocator); + // Add to the list so that we know to free the data later. + class_loaders_.push_back(data); +} + +ClassTable* ClassLinker::InsertClassTableForClassLoader(ObjPtr class_loader) { + if (class_loader == nullptr) { + return boot_class_table_.get(); + } + ClassTable* class_table = class_loader->GetClassTable(); + if (class_table == nullptr) { + RegisterClassLoader(class_loader); + class_table = class_loader->GetClassTable(); + DCHECK(class_table != nullptr); + } + return class_table; +} + +ClassTable* ClassLinker::ClassTableForClassLoader(ObjPtr class_loader) { + return class_loader == nullptr ? boot_class_table_.get() : class_loader->GetClassTable(); +} + +static ImTable* FindSuperImt(ObjPtr klass, PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_) { + while (klass->HasSuperClass()) { + klass = klass->GetSuperClass(); + if (klass->ShouldHaveImt()) { + return klass->GetImt(pointer_size); + } + } + return nullptr; +} + +bool ClassLinker::LinkClass(Thread* self, + const char* descriptor, + Handle klass, + Handle> interfaces, + MutableHandle* h_new_class_out) { + CHECK_EQ(ClassStatus::kLoaded, klass->GetStatus()); + + if (!LinkSuperClass(klass)) { + return false; + } + ArtMethod* imt_data[ImTable::kSize]; + // If there are any new conflicts compared to super class. + bool new_conflict = false; + std::fill_n(imt_data, arraysize(imt_data), Runtime::Current()->GetImtUnimplementedMethod()); + if (!LinkMethods(self, klass, interfaces, &new_conflict, imt_data)) { + return false; + } + if (!LinkInstanceFields(self, klass)) { + return false; + } + size_t class_size; + if (!LinkStaticFields(self, klass, &class_size)) { + return false; + } + CreateReferenceInstanceOffsets(klass); + CHECK_EQ(ClassStatus::kLoaded, klass->GetStatus()); + + ImTable* imt = nullptr; + if (klass->ShouldHaveImt()) { + // If there are any new conflicts compared to the super class we can not make a copy. There + // can be cases where both will have a conflict method at the same slot without having the same + // set of conflicts. In this case, we can not share the IMT since the conflict table slow path + // will possibly create a table that is incorrect for either of the classes. + // Same IMT with new_conflict does not happen very often. + if (!new_conflict) { + ImTable* super_imt = FindSuperImt(klass.Get(), image_pointer_size_); + if (super_imt != nullptr) { + bool imt_equals = true; + for (size_t i = 0; i < ImTable::kSize && imt_equals; ++i) { + imt_equals = imt_equals && (super_imt->Get(i, image_pointer_size_) == imt_data[i]); + } + if (imt_equals) { + imt = super_imt; + } + } + } + if (imt == nullptr) { + LinearAlloc* allocator = GetAllocatorForClassLoader(klass->GetClassLoader()); + imt = reinterpret_cast( + allocator->Alloc(self, ImTable::SizeInBytes(image_pointer_size_))); + if (imt == nullptr) { + return false; + } + imt->Populate(imt_data, image_pointer_size_); + } + } + + if (!klass->IsTemp() || (!init_done_ && klass->GetClassSize() == class_size)) { + // We don't need to retire this class as it has no embedded tables or it was created the + // correct size during class linker initialization. + CHECK_EQ(klass->GetClassSize(), class_size) << klass->PrettyDescriptor(); + + if (klass->ShouldHaveEmbeddedVTable()) { + klass->PopulateEmbeddedVTable(image_pointer_size_); + } + if (klass->ShouldHaveImt()) { + klass->SetImt(imt, image_pointer_size_); + } + + // Update CHA info based on whether we override methods. + // Have to do this before setting the class as resolved which allows + // instantiation of klass. + if (LIKELY(descriptor != nullptr) && cha_ != nullptr) { + cha_->UpdateAfterLoadingOf(klass); + } + + // This will notify waiters on klass that saw the not yet resolved + // class in the class_table_ during EnsureResolved. + mirror::Class::SetStatus(klass, ClassStatus::kResolved, self); + h_new_class_out->Assign(klass.Get()); + } else { + CHECK(!klass->IsResolved()); + // Retire the temporary class and create the correctly sized resolved class. + StackHandleScope<1> hs(self); + Handle h_new_class = + hs.NewHandle(mirror::Class::CopyOf(klass, self, class_size, imt, image_pointer_size_)); + // Set arrays to null since we don't want to have multiple classes with the same ArtField or + // ArtMethod array pointers. If this occurs, it causes bugs in remembered sets since the GC + // may not see any references to the target space and clean the card for a class if another + // class had the same array pointer. + klass->SetMethodsPtrUnchecked(nullptr, 0, 0); + klass->SetSFieldsPtrUnchecked(nullptr); + klass->SetIFieldsPtrUnchecked(nullptr); + if (UNLIKELY(h_new_class == nullptr)) { + self->AssertPendingOOMException(); + mirror::Class::SetStatus(klass, ClassStatus::kErrorUnresolved, self); + return false; + } + + CHECK_EQ(h_new_class->GetClassSize(), class_size); + ObjectLock lock(self, h_new_class); + FixupTemporaryDeclaringClass(klass.Get(), h_new_class.Get()); + + if (LIKELY(descriptor != nullptr)) { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + const ObjPtr class_loader = h_new_class.Get()->GetClassLoader(); + ClassTable* const table = InsertClassTableForClassLoader(class_loader); + const ObjPtr existing = + table->UpdateClass(descriptor, h_new_class.Get(), ComputeModifiedUtf8Hash(descriptor)); + if (class_loader != nullptr) { + // We updated the class in the class table, perform the write barrier so that the GC knows + // about the change. + WriteBarrier::ForEveryFieldWrite(class_loader); + } + CHECK_EQ(existing, klass.Get()); + if (log_new_roots_) { + new_class_roots_.push_back(GcRoot(h_new_class.Get())); + } + } + + // Update CHA info based on whether we override methods. + // Have to do this before setting the class as resolved which allows + // instantiation of klass. + if (LIKELY(descriptor != nullptr) && cha_ != nullptr) { + cha_->UpdateAfterLoadingOf(h_new_class); + } + + // This will notify waiters on temp class that saw the not yet resolved class in the + // class_table_ during EnsureResolved. + mirror::Class::SetStatus(klass, ClassStatus::kRetired, self); + + CHECK_EQ(h_new_class->GetStatus(), ClassStatus::kResolving); + // This will notify waiters on new_class that saw the not yet resolved + // class in the class_table_ during EnsureResolved. + mirror::Class::SetStatus(h_new_class, ClassStatus::kResolved, self); + // Return the new class. + h_new_class_out->Assign(h_new_class.Get()); + } + return true; +} + +bool ClassLinker::LoadSuperAndInterfaces(Handle klass, const DexFile& dex_file) { + CHECK_EQ(ClassStatus::kIdx, klass->GetStatus()); + const dex::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex()); + dex::TypeIndex super_class_idx = class_def.superclass_idx_; + if (super_class_idx.IsValid()) { + // Check that a class does not inherit from itself directly. + // + // TODO: This is a cheap check to detect the straightforward case + // of a class extending itself (b/28685551), but we should do a + // proper cycle detection on loaded classes, to detect all cases + // of class circularity errors (b/28830038). + if (super_class_idx == class_def.class_idx_) { + ThrowClassCircularityError(klass.Get(), + "Class %s extends itself", + klass->PrettyDescriptor().c_str()); + return false; + } + + ObjPtr super_class = ResolveType(super_class_idx, klass.Get()); + if (super_class == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return false; + } + // Verify + if (!klass->CanAccess(super_class)) { + ThrowIllegalAccessError(klass.Get(), "Class %s extended by class %s is inaccessible", + super_class->PrettyDescriptor().c_str(), + klass->PrettyDescriptor().c_str()); + return false; + } + CHECK(super_class->IsResolved()); + klass->SetSuperClass(super_class); + } + const dex::TypeList* interfaces = dex_file.GetInterfacesList(class_def); + if (interfaces != nullptr) { + for (size_t i = 0; i < interfaces->Size(); i++) { + dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_; + ObjPtr interface = ResolveType(idx, klass.Get()); + if (interface == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return false; + } + // Verify + if (!klass->CanAccess(interface)) { + // TODO: the RI seemed to ignore this in my testing. + ThrowIllegalAccessError(klass.Get(), + "Interface %s implemented by class %s is inaccessible", + interface->PrettyDescriptor().c_str(), + klass->PrettyDescriptor().c_str()); + return false; + } + } + } + // Mark the class as loaded. + mirror::Class::SetStatus(klass, ClassStatus::kLoaded, nullptr); + return true; +} + +bool ClassLinker::LinkSuperClass(Handle klass) { + CHECK(!klass->IsPrimitive()); + ObjPtr super = klass->GetSuperClass(); + ObjPtr object_class = GetClassRoot(this); + if (klass.Get() == object_class) { + if (super != nullptr) { + ThrowClassFormatError(klass.Get(), "java.lang.Object must not have a superclass"); + return false; + } + return true; + } + if (super == nullptr) { + ThrowLinkageError(klass.Get(), "No superclass defined for class %s", + klass->PrettyDescriptor().c_str()); + return false; + } + // Verify + if (klass->IsInterface() && super != object_class) { + ThrowClassFormatError(klass.Get(), "Interfaces must have java.lang.Object as superclass"); + return false; + } + if (super->IsFinal()) { + ThrowVerifyError(klass.Get(), + "Superclass %s of %s is declared final", + super->PrettyDescriptor().c_str(), + klass->PrettyDescriptor().c_str()); + return false; + } + if (super->IsInterface()) { + ThrowIncompatibleClassChangeError(klass.Get(), + "Superclass %s of %s is an interface", + super->PrettyDescriptor().c_str(), + klass->PrettyDescriptor().c_str()); + return false; + } + if (!klass->CanAccess(super)) { + ThrowIllegalAccessError(klass.Get(), "Superclass %s is inaccessible to class %s", + super->PrettyDescriptor().c_str(), + klass->PrettyDescriptor().c_str()); + return false; + } + + // Inherit kAccClassIsFinalizable from the superclass in case this + // class doesn't override finalize. + if (super->IsFinalizable()) { + klass->SetFinalizable(); + } + + // Inherit class loader flag form super class. + if (super->IsClassLoaderClass()) { + klass->SetClassLoaderClass(); + } + + // Inherit reference flags (if any) from the superclass. + uint32_t reference_flags = (super->GetClassFlags() & mirror::kClassFlagReference); + if (reference_flags != 0) { + CHECK_EQ(klass->GetClassFlags(), 0u); + klass->SetClassFlags(klass->GetClassFlags() | reference_flags); + } + // Disallow custom direct subclasses of java.lang.ref.Reference. + if (init_done_ && super == GetClassRoot(this)) { + ThrowLinkageError(klass.Get(), + "Class %s attempts to subclass java.lang.ref.Reference, which is not allowed", + klass->PrettyDescriptor().c_str()); + return false; + } + + if (kIsDebugBuild) { + // Ensure super classes are fully resolved prior to resolving fields.. + while (super != nullptr) { + CHECK(super->IsResolved()); + super = super->GetSuperClass(); + } + } + return true; +} + +// A wrapper class representing the result of a method translation used for linking methods and +// updating superclass default methods. For each method in a classes vtable there are 4 states it +// could be in: +// 1) No translation is necessary. In this case there is no MethodTranslation object for it. This +// is the standard case and is true when the method is not overridable by a default method, +// the class defines a concrete implementation of the method, the default method implementation +// remains the same, or an abstract method stayed abstract. +// 2) The method must be translated to a different default method. We note this with +// CreateTranslatedMethod. +// 3) The method must be replaced with a conflict method. This happens when a superclass +// implements an interface with a default method and this class implements an unrelated +// interface that also defines that default method. We note this with CreateConflictingMethod. +// 4) The method must be replaced with an abstract miranda method. This happens when a superclass +// implements an interface with a default method and this class implements a subinterface of +// the superclass's interface which declares the default method abstract. We note this with +// CreateAbstractMethod. +// +// When a method translation is unnecessary (case #1), we don't put it into the +// default_translation maps. So an instance of MethodTranslation must be in one of #2-#4. +class ClassLinker::MethodTranslation { + public: + MethodTranslation() : translation_(nullptr), type_(Type::kInvalid) {} + + // This slot must become a default conflict method. + static MethodTranslation CreateConflictingMethod() { + return MethodTranslation(Type::kConflict, /*translation=*/nullptr); + } + + // This slot must become an abstract method. + static MethodTranslation CreateAbstractMethod() { + return MethodTranslation(Type::kAbstract, /*translation=*/nullptr); + } + + // Use the given method as the current value for this vtable slot during translation. + static MethodTranslation CreateTranslatedMethod(ArtMethod* new_method) { + return MethodTranslation(Type::kTranslation, new_method); + } + + // Returns true if this is a method that must become a conflict method. + bool IsInConflict() const { + return type_ == Type::kConflict; + } + + // Returns true if this is a method that must become an abstract method. + bool IsAbstract() const { + return type_ == Type::kAbstract; + } + + // Returns true if this is a method that must become a different method. + bool IsTranslation() const { + return type_ == Type::kTranslation; + } + + // Get the translated version of this method. + ArtMethod* GetTranslation() const { + DCHECK(IsTranslation()); + DCHECK(translation_ != nullptr); + return translation_; + } + + private: + enum class Type { + kInvalid, + kTranslation, + kConflict, + kAbstract, + }; + + MethodTranslation(Type type, ArtMethod* translation) + : translation_(translation), type_(type) {} + + ArtMethod* translation_; + Type type_; +}; + +// Populate the class vtable and itable. Compute return type indices. +bool ClassLinker::LinkMethods(Thread* self, + Handle klass, + Handle> interfaces, + bool* out_new_conflict, + ArtMethod** out_imt) { + self->AllowThreadSuspension(); + // A map from vtable indexes to the method they need to be updated to point to. Used because we + // need to have default methods be in the virtuals array of each class but we don't set that up + // until LinkInterfaceMethods. + constexpr size_t kBufferSize = 8; // Avoid malloc/free for a few translations. + std::pair buffer[kBufferSize]; + HashMap default_translations(buffer, kBufferSize); + // Link virtual methods then interface methods. + // We set up the interface lookup table first because we need it to determine if we need to update + // any vtable entries with new default method implementations. + return SetupInterfaceLookupTable(self, klass, interfaces) + && LinkVirtualMethods(self, klass, /*out*/ &default_translations) + && LinkInterfaceMethods(self, klass, default_translations, out_new_conflict, out_imt); +} + +// Comparator for name and signature of a method, used in finding overriding methods. Implementation +// avoids the use of handles, if it didn't then rather than compare dex files we could compare dex +// caches in the implementation below. +class MethodNameAndSignatureComparator final : public ValueObject { + public: + explicit MethodNameAndSignatureComparator(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_) : + dex_file_(method->GetDexFile()), mid_(&dex_file_->GetMethodId(method->GetDexMethodIndex())), + name_(nullptr), name_len_(0) { + DCHECK(!method->IsProxyMethod()) << method->PrettyMethod(); + } + + const char* GetName() { + if (name_ == nullptr) { + name_ = dex_file_->StringDataAndUtf16LengthByIdx(mid_->name_idx_, &name_len_); + } + return name_; + } + + bool HasSameNameAndSignature(ArtMethod* other) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!other->IsProxyMethod()) << other->PrettyMethod(); + const DexFile* other_dex_file = other->GetDexFile(); + const dex::MethodId& other_mid = other_dex_file->GetMethodId(other->GetDexMethodIndex()); + if (dex_file_ == other_dex_file) { + return mid_->name_idx_ == other_mid.name_idx_ && mid_->proto_idx_ == other_mid.proto_idx_; + } + GetName(); // Only used to make sure its calculated. + uint32_t other_name_len; + const char* other_name = other_dex_file->StringDataAndUtf16LengthByIdx(other_mid.name_idx_, + &other_name_len); + if (name_len_ != other_name_len || strcmp(name_, other_name) != 0) { + return false; + } + return dex_file_->GetMethodSignature(*mid_) == other_dex_file->GetMethodSignature(other_mid); + } + + private: + // Dex file for the method to compare against. + const DexFile* const dex_file_; + // MethodId for the method to compare against. + const dex::MethodId* const mid_; + // Lazily computed name from the dex file's strings. + const char* name_; + // Lazily computed name length. + uint32_t name_len_; +}; + +class LinkVirtualHashTable { + public: + LinkVirtualHashTable(Handle klass, + size_t hash_size, + uint32_t* hash_table, + PointerSize image_pointer_size) + : klass_(klass), + hash_size_(hash_size), + hash_table_(hash_table), + image_pointer_size_(image_pointer_size) { + std::fill(hash_table_, hash_table_ + hash_size_, invalid_index_); + } + + void Add(uint32_t virtual_method_index) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* local_method = klass_->GetVirtualMethodDuringLinking( + virtual_method_index, image_pointer_size_); + const char* name = local_method->GetInterfaceMethodIfProxy(image_pointer_size_)->GetName(); + uint32_t hash = ComputeModifiedUtf8Hash(name); + uint32_t index = hash % hash_size_; + // Linear probe until we have an empty slot. + while (hash_table_[index] != invalid_index_) { + if (++index == hash_size_) { + index = 0; + } + } + hash_table_[index] = virtual_method_index; + } + + uint32_t FindAndRemove(MethodNameAndSignatureComparator* comparator, uint32_t hash) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_EQ(hash, ComputeModifiedUtf8Hash(comparator->GetName())); + size_t index = hash % hash_size_; + while (true) { + const uint32_t value = hash_table_[index]; + // Since linear probe makes continuous blocks, hitting an invalid index means we are done + // the block and can safely assume not found. + if (value == invalid_index_) { + break; + } + if (value != removed_index_) { // This signifies not already overriden. + ArtMethod* virtual_method = + klass_->GetVirtualMethodDuringLinking(value, image_pointer_size_); + if (comparator->HasSameNameAndSignature( + virtual_method->GetInterfaceMethodIfProxy(image_pointer_size_))) { + hash_table_[index] = removed_index_; + return value; + } + } + if (++index == hash_size_) { + index = 0; + } + } + return GetNotFoundIndex(); + } + + static uint32_t GetNotFoundIndex() { + return invalid_index_; + } + + private: + static const uint32_t invalid_index_; + static const uint32_t removed_index_; + + Handle klass_; + const size_t hash_size_; + uint32_t* const hash_table_; + const PointerSize image_pointer_size_; +}; + +const uint32_t LinkVirtualHashTable::invalid_index_ = std::numeric_limits::max(); +const uint32_t LinkVirtualHashTable::removed_index_ = std::numeric_limits::max() - 1; + +bool ClassLinker::LinkVirtualMethods( + Thread* self, + Handle klass, + /*out*/HashMap* default_translations) { + const size_t num_virtual_methods = klass->NumVirtualMethods(); + if (klass->IsInterface()) { + // No vtable. + if (!IsUint<16>(num_virtual_methods)) { + ThrowClassFormatError(klass.Get(), "Too many methods on interface: %zu", num_virtual_methods); + return false; + } + bool has_defaults = false; + // Assign each method an IMT index and set the default flag. + for (size_t i = 0; i < num_virtual_methods; ++i) { + ArtMethod* m = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_); + m->SetMethodIndex(i); + if (!m->IsAbstract()) { + // If the dex file does not support default methods, throw ClassFormatError. + // This check is necessary to protect from odd cases, such as native default + // methods, that the dex file verifier permits for old dex file versions. b/157170505 + // FIXME: This should be `if (!m->GetDexFile()->SupportsDefaultMethods())` but we're + // currently running CTS tests for default methods with dex file version 035 which + // does not support default methods. So, we limit this to native methods. b/157718952 + if (m->IsNative()) { + DCHECK(!m->GetDexFile()->SupportsDefaultMethods()); + ThrowClassFormatError(klass.Get(), + "Dex file does not support default method '%s'", + m->PrettyMethod().c_str()); + return false; + } + m->SetAccessFlags(m->GetAccessFlags() | kAccDefault); + has_defaults = true; + } + } + // Mark that we have default methods so that we won't need to scan the virtual_methods_ array + // during initialization. This is a performance optimization. We could simply traverse the + // virtual_methods_ array again during initialization. + if (has_defaults) { + klass->SetHasDefaultMethods(); + } + return true; + } else if (klass->HasSuperClass()) { + const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength(); + const size_t max_count = num_virtual_methods + super_vtable_length; + StackHandleScope<3> hs(self); + Handle super_class(hs.NewHandle(klass->GetSuperClass())); + MutableHandle vtable; + if (super_class->ShouldHaveEmbeddedVTable()) { + vtable = hs.NewHandle(AllocPointerArray(self, max_count)); + if (UNLIKELY(vtable == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + for (size_t i = 0; i < super_vtable_length; i++) { + vtable->SetElementPtrSize( + i, super_class->GetEmbeddedVTableEntry(i, image_pointer_size_), image_pointer_size_); + } + // We might need to change vtable if we have new virtual methods or new interfaces (since that + // might give us new default methods). If no new interfaces then we can skip the rest since + // the class cannot override any of the super-class's methods. This is required for + // correctness since without it we might not update overridden default method vtable entries + // correctly. + if (num_virtual_methods == 0 && super_class->GetIfTableCount() == klass->GetIfTableCount()) { + klass->SetVTable(vtable.Get()); + return true; + } + } else { + DCHECK(super_class->IsAbstract() && !super_class->IsArrayClass()); + Handle super_vtable = hs.NewHandle(super_class->GetVTable()); + CHECK(super_vtable != nullptr) << super_class->PrettyClass(); + // We might need to change vtable if we have new virtual methods or new interfaces (since that + // might give us new default methods). See comment above. + if (num_virtual_methods == 0 && super_class->GetIfTableCount() == klass->GetIfTableCount()) { + klass->SetVTable(super_vtable.Get()); + return true; + } + vtable = hs.NewHandle(ObjPtr::DownCast( + mirror::Array::CopyOf(super_vtable, self, max_count))); + if (UNLIKELY(vtable == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + } + // How the algorithm works: + // 1. Populate hash table by adding num_virtual_methods from klass. The values in the hash + // table are: invalid_index for unused slots, index super_vtable_length + i for a virtual + // method which has not been matched to a vtable method, and j if the virtual method at the + // index overrode the super virtual method at index j. + // 2. Loop through super virtual methods, if they overwrite, update hash table to j + // (j < super_vtable_length) to avoid redundant checks. (TODO maybe use this info for reducing + // the need for the initial vtable which we later shrink back down). + // 3. Add non overridden methods to the end of the vtable. + static constexpr size_t kMaxStackHash = 250; + // + 1 so that even if we only have new default methods we will still be able to use this hash + // table (i.e. it will never have 0 size). + const size_t hash_table_size = num_virtual_methods * 3 + 1; + uint32_t* hash_table_ptr; + std::unique_ptr hash_heap_storage; + if (hash_table_size <= kMaxStackHash) { + hash_table_ptr = reinterpret_cast( + alloca(hash_table_size * sizeof(*hash_table_ptr))); + } else { + hash_heap_storage.reset(new uint32_t[hash_table_size]); + hash_table_ptr = hash_heap_storage.get(); + } + LinkVirtualHashTable hash_table(klass, hash_table_size, hash_table_ptr, image_pointer_size_); + // Add virtual methods to the hash table. + for (size_t i = 0; i < num_virtual_methods; ++i) { + DCHECK(klass->GetVirtualMethodDuringLinking( + i, image_pointer_size_)->GetDeclaringClass() != nullptr); + hash_table.Add(i); + } + // Loop through each super vtable method and see if they are overridden by a method we added to + // the hash table. + for (size_t j = 0; j < super_vtable_length; ++j) { + // Search the hash table to see if we are overridden by any method. + ArtMethod* super_method = vtable->GetElementPtrSize(j, image_pointer_size_); + if (!klass->CanAccessMember(super_method->GetDeclaringClass(), + super_method->GetAccessFlags())) { + // Continue on to the next method since this one is package private and canot be overridden. + // Before Android 4.1, the package-private method super_method might have been incorrectly + // overridden. + continue; + } + MethodNameAndSignatureComparator super_method_name_comparator( + super_method->GetInterfaceMethodIfProxy(image_pointer_size_)); + // We remove the method so that subsequent lookups will be faster by making the hash-map + // smaller as we go on. + uint32_t hash = (j < mirror::Object::kVTableLength) + ? object_virtual_method_hashes_[j] + : ComputeModifiedUtf8Hash(super_method_name_comparator.GetName()); + uint32_t hash_index = hash_table.FindAndRemove(&super_method_name_comparator, hash); + if (hash_index != hash_table.GetNotFoundIndex()) { + ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking( + hash_index, image_pointer_size_); + if (super_method->IsFinal()) { + ThrowLinkageError(klass.Get(), "Method %s overrides final method in class %s", + virtual_method->PrettyMethod().c_str(), + super_method->GetDeclaringClassDescriptor()); + return false; + } + vtable->SetElementPtrSize(j, virtual_method, image_pointer_size_); + virtual_method->SetMethodIndex(j); + } else if (super_method->IsOverridableByDefaultMethod()) { + // We didn't directly override this method but we might through default methods... + // Check for default method update. + ArtMethod* default_method = nullptr; + switch (FindDefaultMethodImplementation(self, + super_method, + klass, + /*out*/&default_method)) { + case DefaultMethodSearchResult::kDefaultConflict: { + // A conflict was found looking for default methods. Note this (assuming it wasn't + // pre-existing) in the translations map. + if (UNLIKELY(!super_method->IsDefaultConflicting())) { + // Don't generate another conflict method to reduce memory use as an optimization. + default_translations->insert( + {j, ClassLinker::MethodTranslation::CreateConflictingMethod()}); + } + break; + } + case DefaultMethodSearchResult::kAbstractFound: { + // No conflict but method is abstract. + // We note that this vtable entry must be made abstract. + if (UNLIKELY(!super_method->IsAbstract())) { + default_translations->insert( + {j, ClassLinker::MethodTranslation::CreateAbstractMethod()}); + } + break; + } + case DefaultMethodSearchResult::kDefaultFound: { + if (UNLIKELY(super_method->IsDefaultConflicting() || + default_method->GetDeclaringClass() != super_method->GetDeclaringClass())) { + // Found a default method implementation that is new. + // TODO Refactor this add default methods to virtuals here and not in + // LinkInterfaceMethods maybe. + // The problem is default methods might override previously present + // default-method or miranda-method vtable entries from the superclass. + // Unfortunately we need these to be entries in this class's virtuals. We do not + // give these entries there until LinkInterfaceMethods so we pass this map around + // to let it know which vtable entries need to be updated. + // Make a note that vtable entry j must be updated, store what it needs to be updated + // to. We will allocate a virtual method slot in LinkInterfaceMethods and fix it up + // then. + default_translations->insert( + {j, ClassLinker::MethodTranslation::CreateTranslatedMethod(default_method)}); + VLOG(class_linker) << "Method " << super_method->PrettyMethod() + << " overridden by default " + << default_method->PrettyMethod() + << " in " << mirror::Class::PrettyClass(klass.Get()); + } + break; + } + } + } + } + size_t actual_count = super_vtable_length; + // Add the non-overridden methods at the end. + for (size_t i = 0; i < num_virtual_methods; ++i) { + ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_); + size_t method_idx = local_method->GetMethodIndexDuringLinking(); + if (method_idx < super_vtable_length && + local_method == vtable->GetElementPtrSize(method_idx, image_pointer_size_)) { + continue; + } + vtable->SetElementPtrSize(actual_count, local_method, image_pointer_size_); + local_method->SetMethodIndex(actual_count); + ++actual_count; + } + if (!IsUint<16>(actual_count)) { + ThrowClassFormatError(klass.Get(), "Too many methods defined on class: %zd", actual_count); + return false; + } + // Shrink vtable if possible + CHECK_LE(actual_count, max_count); + if (actual_count < max_count) { + vtable.Assign(ObjPtr::DownCast( + mirror::Array::CopyOf(vtable, self, actual_count))); + if (UNLIKELY(vtable == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + } + klass->SetVTable(vtable.Get()); + } else { + CHECK_EQ(klass.Get(), GetClassRoot(this)); + if (!IsUint<16>(num_virtual_methods)) { + ThrowClassFormatError(klass.Get(), "Too many methods: %d", + static_cast(num_virtual_methods)); + return false; + } + ObjPtr vtable = AllocPointerArray(self, num_virtual_methods); + if (UNLIKELY(vtable == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + for (size_t i = 0; i < num_virtual_methods; ++i) { + ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_); + vtable->SetElementPtrSize(i, virtual_method, image_pointer_size_); + virtual_method->SetMethodIndex(i & 0xFFFF); + } + klass->SetVTable(vtable); + InitializeObjectVirtualMethodHashes(klass.Get(), + image_pointer_size_, + ArrayRef(object_virtual_method_hashes_)); + } + return true; +} + +// Determine if the given iface has any subinterface in the given list that declares the method +// specified by 'target'. +// +// Arguments +// - self: The thread we are running on +// - target: A comparator that will match any method that overrides the method we are checking for +// - iftable: The iftable we are searching for an overriding method on. +// - ifstart: The index of the interface we are checking to see if anything overrides +// - iface: The interface we are checking to see if anything overrides. +// - image_pointer_size: +// The image pointer size. +// +// Returns +// - True: There is some method that matches the target comparator defined in an interface that +// is a subtype of iface. +// - False: There is no method that matches the target comparator in any interface that is a subtype +// of iface. +static bool ContainsOverridingMethodOf(Thread* self, + MethodNameAndSignatureComparator& target, + Handle iftable, + size_t ifstart, + Handle iface, + PointerSize image_pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(self != nullptr); + DCHECK(iface != nullptr); + DCHECK(iftable != nullptr); + DCHECK_GE(ifstart, 0u); + DCHECK_LT(ifstart, iftable->Count()); + DCHECK_EQ(iface.Get(), iftable->GetInterface(ifstart)); + DCHECK(iface->IsInterface()); + + size_t iftable_count = iftable->Count(); + StackHandleScope<1> hs(self); + MutableHandle current_iface(hs.NewHandle(nullptr)); + for (size_t k = ifstart + 1; k < iftable_count; k++) { + // Skip ifstart since our current interface obviously cannot override itself. + current_iface.Assign(iftable->GetInterface(k)); + // Iterate through every method on this interface. The order does not matter. + for (ArtMethod& current_method : current_iface->GetDeclaredVirtualMethods(image_pointer_size)) { + if (UNLIKELY(target.HasSameNameAndSignature( + current_method.GetInterfaceMethodIfProxy(image_pointer_size)))) { + // Check if the i'th interface is a subtype of this one. + if (iface->IsAssignableFrom(current_iface.Get())) { + return true; + } + break; + } + } + } + return false; +} + +// Find the default method implementation for 'interface_method' in 'klass'. Stores it into +// out_default_method and returns kDefaultFound on success. If no default method was found return +// kAbstractFound and store nullptr into out_default_method. If an error occurs (such as a +// default_method conflict) it will return kDefaultConflict. +ClassLinker::DefaultMethodSearchResult ClassLinker::FindDefaultMethodImplementation( + Thread* self, + ArtMethod* target_method, + Handle klass, + /*out*/ArtMethod** out_default_method) const { + DCHECK(self != nullptr); + DCHECK(target_method != nullptr); + DCHECK(out_default_method != nullptr); + + *out_default_method = nullptr; + + // We organize the interface table so that, for interface I any subinterfaces J follow it in the + // table. This lets us walk the table backwards when searching for default methods. The first one + // we encounter is the best candidate since it is the most specific. Once we have found it we keep + // track of it and then continue checking all other interfaces, since we need to throw an error if + // we encounter conflicting default method implementations (one is not a subtype of the other). + // + // The order of unrelated interfaces does not matter and is not defined. + size_t iftable_count = klass->GetIfTableCount(); + if (iftable_count == 0) { + // No interfaces. We have already reset out to null so just return kAbstractFound. + return DefaultMethodSearchResult::kAbstractFound; + } + + StackHandleScope<3> hs(self); + MutableHandle chosen_iface(hs.NewHandle(nullptr)); + MutableHandle iftable(hs.NewHandle(klass->GetIfTable())); + MutableHandle iface(hs.NewHandle(nullptr)); + MethodNameAndSignatureComparator target_name_comparator( + target_method->GetInterfaceMethodIfProxy(image_pointer_size_)); + // Iterates over the klass's iftable in reverse + for (size_t k = iftable_count; k != 0; ) { + --k; + + DCHECK_LT(k, iftable->Count()); + + iface.Assign(iftable->GetInterface(k)); + // Iterate through every declared method on this interface. The order does not matter. + for (auto& method_iter : iface->GetDeclaredVirtualMethods(image_pointer_size_)) { + ArtMethod* current_method = &method_iter; + // Skip abstract methods and methods with different names. + if (current_method->IsAbstract() || + !target_name_comparator.HasSameNameAndSignature( + current_method->GetInterfaceMethodIfProxy(image_pointer_size_))) { + continue; + } else if (!current_method->IsPublic()) { + // The verifier should have caught the non-public method for dex version 37. Just warn and + // skip it since this is from before default-methods so we don't really need to care that it + // has code. + LOG(WARNING) << "Interface method " << current_method->PrettyMethod() + << " is not public! " + << "This will be a fatal error in subsequent versions of android. " + << "Continuing anyway."; + } + if (UNLIKELY(chosen_iface != nullptr)) { + // We have multiple default impls of the same method. This is a potential default conflict. + // We need to check if this possibly conflicting method is either a superclass of the chosen + // default implementation or is overridden by a non-default interface method. In either case + // there is no conflict. + if (!iface->IsAssignableFrom(chosen_iface.Get()) && + !ContainsOverridingMethodOf(self, + target_name_comparator, + iftable, + k, + iface, + image_pointer_size_)) { + VLOG(class_linker) << "Conflicting default method implementations found: " + << current_method->PrettyMethod() << " and " + << ArtMethod::PrettyMethod(*out_default_method) << " in class " + << klass->PrettyClass() << " conflict."; + *out_default_method = nullptr; + return DefaultMethodSearchResult::kDefaultConflict; + } else { + break; // Continue checking at the next interface. + } + } else { + // chosen_iface == null + if (!ContainsOverridingMethodOf(self, + target_name_comparator, + iftable, + k, + iface, + image_pointer_size_)) { + // Don't set this as the chosen interface if something else is overriding it (because that + // other interface would be potentially chosen instead if it was default). If the other + // interface was abstract then we wouldn't select this interface as chosen anyway since + // the abstract method masks it. + *out_default_method = current_method; + chosen_iface.Assign(iface.Get()); + // We should now finish traversing the graph to find if we have default methods that + // conflict. + } else { + VLOG(class_linker) << "A default method '" << current_method->PrettyMethod() + << "' was " + << "skipped because it was overridden by an abstract method in a " + << "subinterface on class '" << klass->PrettyClass() << "'"; + } + } + break; + } + } + if (*out_default_method != nullptr) { + VLOG(class_linker) << "Default method '" << (*out_default_method)->PrettyMethod() + << "' selected " + << "as the implementation for '" << target_method->PrettyMethod() + << "' in '" << klass->PrettyClass() << "'"; + return DefaultMethodSearchResult::kDefaultFound; + } else { + return DefaultMethodSearchResult::kAbstractFound; + } +} + +ArtMethod* ClassLinker::AddMethodToConflictTable(ObjPtr klass, + ArtMethod* conflict_method, + ArtMethod* interface_method, + ArtMethod* method) { + ImtConflictTable* current_table = conflict_method->GetImtConflictTable(kRuntimePointerSize); + Runtime* const runtime = Runtime::Current(); + LinearAlloc* linear_alloc = GetAllocatorForClassLoader(klass->GetClassLoader()); + + // Create a new entry if the existing one is the shared conflict method. + ArtMethod* new_conflict_method = (conflict_method == runtime->GetImtConflictMethod()) + ? runtime->CreateImtConflictMethod(linear_alloc) + : conflict_method; + + // Allocate a new table. Note that we will leak this table at the next conflict, + // but that's a tradeoff compared to making the table fixed size. + void* data = linear_alloc->Alloc( + Thread::Current(), ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table, + image_pointer_size_)); + if (data == nullptr) { + LOG(ERROR) << "Failed to allocate conflict table"; + return conflict_method; + } + ImtConflictTable* new_table = new (data) ImtConflictTable(current_table, + interface_method, + method, + image_pointer_size_); + + // Do a fence to ensure threads see the data in the table before it is assigned + // to the conflict method. + // Note that there is a race in the presence of multiple threads and we may leak + // memory from the LinearAlloc, but that's a tradeoff compared to using + // atomic operations. + std::atomic_thread_fence(std::memory_order_release); + new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_); + return new_conflict_method; +} + +bool ClassLinker::AllocateIfTableMethodArrays(Thread* self, + Handle klass, + Handle iftable) { + DCHECK(!klass->IsInterface()); + const bool has_superclass = klass->HasSuperClass(); + const bool extend_super_iftable = has_superclass; + const size_t ifcount = klass->GetIfTableCount(); + const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U; + for (size_t i = 0; i < ifcount; ++i) { + size_t num_methods = iftable->GetInterface(i)->NumDeclaredVirtualMethods(); + if (num_methods > 0) { + const bool is_super = i < super_ifcount; + // This is an interface implemented by a super-class. Therefore we can just copy the method + // array from the superclass. + const bool super_interface = is_super && extend_super_iftable; + ObjPtr method_array; + if (super_interface) { + ObjPtr if_table = klass->GetSuperClass()->GetIfTable(); + DCHECK(if_table != nullptr); + DCHECK(if_table->GetMethodArray(i) != nullptr); + // If we are working on a super interface, try extending the existing method array. + StackHandleScope<1u> hs(self); + Handle old_array = hs.NewHandle(if_table->GetMethodArray(i)); + method_array = + ObjPtr::DownCast(mirror::Object::Clone(old_array, self)); + } else { + method_array = AllocPointerArray(self, num_methods); + } + if (UNLIKELY(method_array == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + iftable->SetMethodArray(i, method_array); + } + } + return true; +} + +void ClassLinker::SetIMTRef(ArtMethod* unimplemented_method, + ArtMethod* imt_conflict_method, + ArtMethod* current_method, + /*out*/bool* new_conflict, + /*out*/ArtMethod** imt_ref) { + // Place method in imt if entry is empty, place conflict otherwise. + if (*imt_ref == unimplemented_method) { + *imt_ref = current_method; + } else if (!(*imt_ref)->IsRuntimeMethod()) { + // If we are not a conflict and we have the same signature and name as the imt + // entry, it must be that we overwrote a superclass vtable entry. + // Note that we have checked IsRuntimeMethod, as there may be multiple different + // conflict methods. + MethodNameAndSignatureComparator imt_comparator( + (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size_)); + if (imt_comparator.HasSameNameAndSignature( + current_method->GetInterfaceMethodIfProxy(image_pointer_size_))) { + *imt_ref = current_method; + } else { + *imt_ref = imt_conflict_method; + *new_conflict = true; + } + } else { + // Place the default conflict method. Note that there may be an existing conflict + // method in the IMT, but it could be one tailored to the super class, with a + // specific ImtConflictTable. + *imt_ref = imt_conflict_method; + *new_conflict = true; + } +} + +void ClassLinker::FillIMTAndConflictTables(ObjPtr klass) { + DCHECK(klass->ShouldHaveImt()) << klass->PrettyClass(); + DCHECK(!klass->IsTemp()) << klass->PrettyClass(); + ArtMethod* imt_data[ImTable::kSize]; + Runtime* const runtime = Runtime::Current(); + ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod(); + ArtMethod* const conflict_method = runtime->GetImtConflictMethod(); + std::fill_n(imt_data, arraysize(imt_data), unimplemented_method); + if (klass->GetIfTable() != nullptr) { + bool new_conflict = false; + FillIMTFromIfTable(klass->GetIfTable(), + unimplemented_method, + conflict_method, + klass, + /*create_conflict_tables=*/true, + /*ignore_copied_methods=*/false, + &new_conflict, + &imt_data[0]); + } + // Compare the IMT with the super class including the conflict methods. If they are equivalent, + // we can just use the same pointer. + ImTable* imt = nullptr; + ObjPtr super_class = klass->GetSuperClass(); + if (super_class != nullptr && super_class->ShouldHaveImt()) { + ImTable* super_imt = super_class->GetImt(image_pointer_size_); + bool same = true; + for (size_t i = 0; same && i < ImTable::kSize; ++i) { + ArtMethod* method = imt_data[i]; + ArtMethod* super_method = super_imt->Get(i, image_pointer_size_); + if (method != super_method) { + bool is_conflict_table = method->IsRuntimeMethod() && + method != unimplemented_method && + method != conflict_method; + // Verify conflict contents. + bool super_conflict_table = super_method->IsRuntimeMethod() && + super_method != unimplemented_method && + super_method != conflict_method; + if (!is_conflict_table || !super_conflict_table) { + same = false; + } else { + ImtConflictTable* table1 = method->GetImtConflictTable(image_pointer_size_); + ImtConflictTable* table2 = super_method->GetImtConflictTable(image_pointer_size_); + same = same && table1->Equals(table2, image_pointer_size_); + } + } + } + if (same) { + imt = super_imt; + } + } + if (imt == nullptr) { + imt = klass->GetImt(image_pointer_size_); + DCHECK(imt != nullptr); + imt->Populate(imt_data, image_pointer_size_); + } else { + klass->SetImt(imt, image_pointer_size_); + } +} + +ImtConflictTable* ClassLinker::CreateImtConflictTable(size_t count, + LinearAlloc* linear_alloc, + PointerSize image_pointer_size) { + void* data = linear_alloc->Alloc(Thread::Current(), + ImtConflictTable::ComputeSize(count, + image_pointer_size)); + return (data != nullptr) ? new (data) ImtConflictTable(count, image_pointer_size) : nullptr; +} + +ImtConflictTable* ClassLinker::CreateImtConflictTable(size_t count, LinearAlloc* linear_alloc) { + return CreateImtConflictTable(count, linear_alloc, image_pointer_size_); +} + +void ClassLinker::FillIMTFromIfTable(ObjPtr if_table, + ArtMethod* unimplemented_method, + ArtMethod* imt_conflict_method, + ObjPtr klass, + bool create_conflict_tables, + bool ignore_copied_methods, + /*out*/bool* new_conflict, + /*out*/ArtMethod** imt) { + uint32_t conflict_counts[ImTable::kSize] = {}; + for (size_t i = 0, length = if_table->Count(); i < length; ++i) { + ObjPtr interface = if_table->GetInterface(i); + const size_t num_virtuals = interface->NumVirtualMethods(); + const size_t method_array_count = if_table->GetMethodArrayCount(i); + // Virtual methods can be larger than the if table methods if there are default methods. + DCHECK_GE(num_virtuals, method_array_count); + if (kIsDebugBuild) { + if (klass->IsInterface()) { + DCHECK_EQ(method_array_count, 0u); + } else { + DCHECK_EQ(interface->NumDeclaredVirtualMethods(), method_array_count); + } + } + if (method_array_count == 0) { + continue; + } + ObjPtr method_array = if_table->GetMethodArray(i); + for (size_t j = 0; j < method_array_count; ++j) { + ArtMethod* implementation_method = + method_array->GetElementPtrSize(j, image_pointer_size_); + if (ignore_copied_methods && implementation_method->IsCopied()) { + continue; + } + DCHECK(implementation_method != nullptr); + // Miranda methods cannot be used to implement an interface method, but they are safe to put + // in the IMT since their entrypoint is the interface trampoline. If we put any copied methods + // or interface methods in the IMT here they will not create extra conflicts since we compare + // names and signatures in SetIMTRef. + ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_); + const uint32_t imt_index = interface_method->GetImtIndex(); + + // There is only any conflicts if all of the interface methods for an IMT slot don't have + // the same implementation method, keep track of this to avoid creating a conflict table in + // this case. + + // Conflict table size for each IMT slot. + ++conflict_counts[imt_index]; + + SetIMTRef(unimplemented_method, + imt_conflict_method, + implementation_method, + /*out*/new_conflict, + /*out*/&imt[imt_index]); + } + } + + if (create_conflict_tables) { + // Create the conflict tables. + LinearAlloc* linear_alloc = GetAllocatorForClassLoader(klass->GetClassLoader()); + for (size_t i = 0; i < ImTable::kSize; ++i) { + size_t conflicts = conflict_counts[i]; + if (imt[i] == imt_conflict_method) { + ImtConflictTable* new_table = CreateImtConflictTable(conflicts, linear_alloc); + if (new_table != nullptr) { + ArtMethod* new_conflict_method = + Runtime::Current()->CreateImtConflictMethod(linear_alloc); + new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_); + imt[i] = new_conflict_method; + } else { + LOG(ERROR) << "Failed to allocate conflict table"; + imt[i] = imt_conflict_method; + } + } else { + DCHECK_NE(imt[i], imt_conflict_method); + } + } + + for (size_t i = 0, length = if_table->Count(); i < length; ++i) { + ObjPtr interface = if_table->GetInterface(i); + const size_t method_array_count = if_table->GetMethodArrayCount(i); + // Virtual methods can be larger than the if table methods if there are default methods. + if (method_array_count == 0) { + continue; + } + ObjPtr method_array = if_table->GetMethodArray(i); + for (size_t j = 0; j < method_array_count; ++j) { + ArtMethod* implementation_method = + method_array->GetElementPtrSize(j, image_pointer_size_); + if (ignore_copied_methods && implementation_method->IsCopied()) { + continue; + } + DCHECK(implementation_method != nullptr); + ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_); + const uint32_t imt_index = interface_method->GetImtIndex(); + if (!imt[imt_index]->IsRuntimeMethod() || + imt[imt_index] == unimplemented_method || + imt[imt_index] == imt_conflict_method) { + continue; + } + ImtConflictTable* table = imt[imt_index]->GetImtConflictTable(image_pointer_size_); + const size_t num_entries = table->NumEntries(image_pointer_size_); + table->SetInterfaceMethod(num_entries, image_pointer_size_, interface_method); + table->SetImplementationMethod(num_entries, image_pointer_size_, implementation_method); + } + } + } +} + +// Simple helper function that checks that no subtypes of 'val' are contained within the 'classes' +// set. +static bool NotSubinterfaceOfAny( + const HashSet& classes, + ObjPtr val) + REQUIRES(Roles::uninterruptible_) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(val != nullptr); + for (ObjPtr c : classes) { + if (val->IsAssignableFrom(c)) { + return false; + } + } + return true; +} + +// Fills in and flattens the interface inheritance hierarchy. +// +// By the end of this function all interfaces in the transitive closure of to_process are added to +// the iftable and every interface precedes all of its sub-interfaces in this list. +// +// all I, J: Interface | I <: J implies J precedes I +// +// (note A <: B means that A is a subtype of B) +// +// This returns the total number of items in the iftable. The iftable might be resized down after +// this call. +// +// We order this backwards so that we do not need to reorder superclass interfaces when new +// interfaces are added in subclass's interface tables. +// +// Upon entry into this function iftable is a copy of the superclass's iftable with the first +// super_ifcount entries filled in with the transitive closure of the interfaces of the superclass. +// The other entries are uninitialized. We will fill in the remaining entries in this function. The +// iftable must be large enough to hold all interfaces without changing its size. +static size_t FillIfTable(Thread* self, + ObjPtr klass, + ObjPtr> interfaces, + ObjPtr iftable, + size_t super_ifcount, + size_t num_interfaces) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedAssertNoThreadSuspension nts(__FUNCTION__); + // This is the set of all classes already in the iftable. Used to make checking + // if a class has already been added quicker. + constexpr size_t kBufferSize = 32; // 256 bytes on 64-bit architectures. + mirror::Class* buffer[kBufferSize]; + HashSet classes_in_iftable(buffer, kBufferSize); + // The first super_ifcount elements are from the superclass. We note that they are already added. + for (size_t i = 0; i < super_ifcount; i++) { + ObjPtr iface = iftable->GetInterface(i); + DCHECK(NotSubinterfaceOfAny(classes_in_iftable, iface)) << "Bad ordering."; + classes_in_iftable.insert(iface.Ptr()); + } + size_t filled_ifcount = super_ifcount; + const bool have_interfaces = interfaces != nullptr; + for (size_t i = 0; i != num_interfaces; ++i) { + ObjPtr interface = have_interfaces + ? interfaces->Get(i) + : mirror::Class::GetDirectInterface(self, klass, i); + + // Let us call the first filled_ifcount elements of iftable the current-iface-list. + // At this point in the loop current-iface-list has the invariant that: + // for every pair of interfaces I,J within it: + // if index_of(I) < index_of(J) then I is not a subtype of J + + // If we have already seen this element then all of its super-interfaces must already be in the + // current-iface-list so we can skip adding it. + if (classes_in_iftable.find(interface.Ptr()) == classes_in_iftable.end()) { + // We haven't seen this interface so add all of its super-interfaces onto the + // current-iface-list, skipping those already on it. + int32_t ifcount = interface->GetIfTableCount(); + for (int32_t j = 0; j < ifcount; j++) { + ObjPtr super_interface = interface->GetIfTable()->GetInterface(j); + if (!ContainsElement(classes_in_iftable, super_interface)) { + DCHECK(NotSubinterfaceOfAny(classes_in_iftable, super_interface)) << "Bad ordering."; + classes_in_iftable.insert(super_interface.Ptr()); + iftable->SetInterface(filled_ifcount, super_interface); + filled_ifcount++; + } + } + DCHECK(NotSubinterfaceOfAny(classes_in_iftable, interface)) << "Bad ordering"; + // Place this interface onto the current-iface-list after all of its super-interfaces. + classes_in_iftable.insert(interface.Ptr()); + iftable->SetInterface(filled_ifcount, interface); + filled_ifcount++; + } else if (kIsDebugBuild) { + // Check all super-interfaces are already in the list. + int32_t ifcount = interface->GetIfTableCount(); + for (int32_t j = 0; j < ifcount; j++) { + ObjPtr super_interface = interface->GetIfTable()->GetInterface(j); + DCHECK(ContainsElement(classes_in_iftable, super_interface)) + << "Iftable does not contain " << mirror::Class::PrettyClass(super_interface) + << ", a superinterface of " << interface->PrettyClass(); + } + } + } + if (kIsDebugBuild) { + // Check that the iftable is ordered correctly. + for (size_t i = 0; i < filled_ifcount; i++) { + ObjPtr if_a = iftable->GetInterface(i); + for (size_t j = i + 1; j < filled_ifcount; j++) { + ObjPtr if_b = iftable->GetInterface(j); + // !(if_a <: if_b) + CHECK(!if_b->IsAssignableFrom(if_a)) + << "Bad interface order: " << mirror::Class::PrettyClass(if_a) << " (index " << i + << ") extends " + << if_b->PrettyClass() << " (index " << j << ") and so should be after it in the " + << "interface list."; + } + } + } + return filled_ifcount; +} + +bool ClassLinker::SetupInterfaceLookupTable(Thread* self, + Handle klass, + Handle> interfaces) { + StackHandleScope<1> hs(self); + const bool has_superclass = klass->HasSuperClass(); + const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U; + const bool have_interfaces = interfaces != nullptr; + const size_t num_interfaces = + have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces(); + if (num_interfaces == 0) { + if (super_ifcount == 0) { + if (LIKELY(has_superclass)) { + klass->SetIfTable(klass->GetSuperClass()->GetIfTable()); + } + // Class implements no interfaces. + DCHECK_EQ(klass->GetIfTableCount(), 0); + return true; + } + // Class implements same interfaces as parent, are any of these not marker interfaces? + bool has_non_marker_interface = false; + ObjPtr super_iftable = klass->GetSuperClass()->GetIfTable(); + for (size_t i = 0; i < super_ifcount; ++i) { + if (super_iftable->GetMethodArrayCount(i) > 0) { + has_non_marker_interface = true; + break; + } + } + // Class just inherits marker interfaces from parent so recycle parent's iftable. + if (!has_non_marker_interface) { + klass->SetIfTable(super_iftable); + return true; + } + } + size_t ifcount = super_ifcount + num_interfaces; + // Check that every class being implemented is an interface. + for (size_t i = 0; i < num_interfaces; i++) { + ObjPtr interface = have_interfaces + ? interfaces->GetWithoutChecks(i) + : mirror::Class::GetDirectInterface(self, klass.Get(), i); + DCHECK(interface != nullptr); + if (UNLIKELY(!interface->IsInterface())) { + std::string temp; + ThrowIncompatibleClassChangeError(klass.Get(), + "Class %s implements non-interface class %s", + klass->PrettyDescriptor().c_str(), + PrettyDescriptor(interface->GetDescriptor(&temp)).c_str()); + return false; + } + ifcount += interface->GetIfTableCount(); + } + // Create the interface function table. + MutableHandle iftable(hs.NewHandle(AllocIfTable(self, ifcount))); + if (UNLIKELY(iftable == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + // Fill in table with superclass's iftable. + if (super_ifcount != 0) { + ObjPtr super_iftable = klass->GetSuperClass()->GetIfTable(); + for (size_t i = 0; i < super_ifcount; i++) { + ObjPtr super_interface = super_iftable->GetInterface(i); + iftable->SetInterface(i, super_interface); + } + } + + // Note that AllowThreadSuspension is to thread suspension as pthread_testcancel is to pthread + // cancellation. That is it will suspend if one has a pending suspend request but otherwise + // doesn't really do anything. + self->AllowThreadSuspension(); + + const size_t new_ifcount = FillIfTable( + self, klass.Get(), interfaces.Get(), iftable.Get(), super_ifcount, num_interfaces); + + self->AllowThreadSuspension(); + + // Shrink iftable in case duplicates were found + if (new_ifcount < ifcount) { + DCHECK_NE(num_interfaces, 0U); + iftable.Assign(ObjPtr::DownCast( + mirror::IfTable::CopyOf(iftable, self, new_ifcount * mirror::IfTable::kMax))); + if (UNLIKELY(iftable == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + ifcount = new_ifcount; + } else { + DCHECK_EQ(new_ifcount, ifcount); + } + klass->SetIfTable(iftable.Get()); + return true; +} + +// Finds the method with a name/signature that matches cmp in the given lists of methods. The list +// of methods must be unique. +static ArtMethod* FindSameNameAndSignature(MethodNameAndSignatureComparator& cmp ATTRIBUTE_UNUSED) { + return nullptr; +} + +template +static ArtMethod* FindSameNameAndSignature(MethodNameAndSignatureComparator& cmp, + const ScopedArenaVector& list, + const Types& ... rest) + REQUIRES_SHARED(Locks::mutator_lock_) { + for (ArtMethod* method : list) { + if (cmp.HasSameNameAndSignature(method)) { + return method; + } + } + return FindSameNameAndSignature(cmp, rest...); +} + +namespace { + +// Check that all vtable entries are present in this class's virtuals or are the same as a +// superclasses vtable entry. +void CheckClassOwnsVTableEntries(Thread* self, + Handle klass, + PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<2> hs(self); + Handle check_vtable(hs.NewHandle(klass->GetVTableDuringLinking())); + ObjPtr super_temp = (klass->HasSuperClass()) ? klass->GetSuperClass() : nullptr; + Handle superclass(hs.NewHandle(super_temp)); + int32_t super_vtable_length = (superclass != nullptr) ? superclass->GetVTableLength() : 0; + for (int32_t i = 0; i < check_vtable->GetLength(); ++i) { + ArtMethod* m = check_vtable->GetElementPtrSize(i, pointer_size); + CHECK(m != nullptr); + + if (m->GetMethodIndexDuringLinking() != i) { + LOG(WARNING) << m->PrettyMethod() + << " has an unexpected method index for its spot in the vtable for class" + << klass->PrettyClass(); + } + ArraySlice virtuals = klass->GetVirtualMethodsSliceUnchecked(pointer_size); + auto is_same_method = [m] (const ArtMethod& meth) { + return &meth == m; + }; + if (!((super_vtable_length > i && superclass->GetVTableEntry(i, pointer_size) == m) || + std::find_if(virtuals.begin(), virtuals.end(), is_same_method) != virtuals.end())) { + LOG(WARNING) << m->PrettyMethod() << " does not seem to be owned by current class " + << klass->PrettyClass() << " or any of its superclasses!"; + } + } +} + +// Check to make sure the vtable does not have duplicates. Duplicates could cause problems when a +// method is overridden in a subclass. +template +void CheckVTableHasNoDuplicates(Thread* self, Handle klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle vtable(hs.NewHandle(klass->GetVTableDuringLinking())); + int32_t num_entries = vtable->GetLength(); + + // Observations: + // * The older implementation was O(n^2) and got too expensive for apps with larger classes. + // * Many classes do not override Object functions (e.g., equals/hashCode/toString). Thus, + // for many classes outside of libcore a cross-dexfile check has to be run anyways. + // * In the cross-dexfile case, with the O(n^2), in the best case O(n) cross checks would have + // to be done. It is thus OK in a single-pass algorithm to read all data, anyways. + // * The single-pass algorithm will trade memory for speed, but that is OK. + + CHECK_GT(num_entries, 0); + + auto log_fn = [&vtable, &klass](int32_t i, int32_t j) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m1 = vtable->GetElementPtrSize(i); + ArtMethod* m2 = vtable->GetElementPtrSize(j); + LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for " + << klass->PrettyClass() << " in method " << m1->PrettyMethod() + << " (0x" << std::hex << reinterpret_cast(m2) << ") and " + << m2->PrettyMethod() << " (0x" << std::hex + << reinterpret_cast(m2) << ")"; + }; + struct BaseHashType { + static size_t HashCombine(size_t seed, size_t val) { + return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2)); + } + }; + + // Check assuming all entries come from the same dex file. + { + // Find the first interesting method and its dex file. + int32_t start = 0; + for (; start < num_entries; ++start) { + ArtMethod* vtable_entry = vtable->GetElementPtrSize(start); + // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member + // maybe). + if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(), + vtable_entry->GetAccessFlags())) { + continue; + } + break; + } + if (start == num_entries) { + return; + } + const DexFile* dex_file = + vtable->GetElementPtrSize(start)-> + GetInterfaceMethodIfProxy(kPointerSize)->GetDexFile(); + + // Helper function to avoid logging if we have to run the cross-file checks. + auto check_fn = [&](bool log_warn) REQUIRES_SHARED(Locks::mutator_lock_) { + // Use a map to store seen entries, as the storage space is too large for a bitvector. + using PairType = std::pair; + struct PairHash : BaseHashType { + size_t operator()(const PairType& key) const { + return BaseHashType::HashCombine(BaseHashType::HashCombine(0, key.first), key.second); + } + }; + HashMap, PairHash> seen; + seen.reserve(2 * num_entries); + bool need_slow_path = false; + bool found_dup = false; + for (int i = start; i < num_entries; ++i) { + // Can use Unchecked here as the start loop already ensured that the arrays are correct + // wrt/ kPointerSize. + ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked(i); + if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(), + vtable_entry->GetAccessFlags())) { + continue; + } + ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize); + if (dex_file != m->GetDexFile()) { + need_slow_path = true; + break; + } + const dex::MethodId* m_mid = &dex_file->GetMethodId(m->GetDexMethodIndex()); + PairType pair = std::make_pair(m_mid->name_idx_.index_, m_mid->proto_idx_.index_); + auto it = seen.find(pair); + if (it != seen.end()) { + found_dup = true; + if (log_warn) { + log_fn(it->second, i); + } + } else { + seen.insert(std::make_pair(pair, i)); + } + } + return std::make_pair(need_slow_path, found_dup); + }; + std::pair result = check_fn(/* log_warn= */ false); + if (!result.first) { + if (result.second) { + check_fn(/* log_warn= */ true); + } + return; + } + } + + // Need to check across dex files. + struct Entry { + size_t cached_hash = 0; + uint32_t name_len = 0; + const char* name = nullptr; + Signature signature = Signature::NoSignature(); + + Entry() = default; + Entry(const Entry& other) = default; + Entry& operator=(const Entry& other) = default; + + Entry(const DexFile* dex_file, const dex::MethodId& mid) + : name_len(0), // Explicit to enforce ordering with -Werror,-Wreorder-ctor. + // This call writes `name_len` and it is therefore necessary that the + // initializer for `name_len` comes before it, otherwise the value + // from the call would be overwritten by that initializer. + name(dex_file->StringDataAndUtf16LengthByIdx(mid.name_idx_, &name_len)), + signature(dex_file->GetMethodSignature(mid)) { + // The `name_len` has been initialized to the UTF16 length. Calculate length in bytes. + if (name[name_len] != 0) { + name_len += strlen(name + name_len); + } + } + + bool operator==(const Entry& other) const { + return name_len == other.name_len && + memcmp(name, other.name, name_len) == 0 && + signature == other.signature; + } + }; + struct EntryHash { + size_t operator()(const Entry& key) const { + return key.cached_hash; + } + }; + HashMap, EntryHash> map; + for (int32_t i = 0; i < num_entries; ++i) { + // Can use Unchecked here as the first loop already ensured that the arrays are correct + // wrt/ kPointerSize. + ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked(i); + // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member + // maybe). + if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(), + vtable_entry->GetAccessFlags())) { + continue; + } + ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize); + const DexFile* dex_file = m->GetDexFile(); + const dex::MethodId& mid = dex_file->GetMethodId(m->GetDexMethodIndex()); + + Entry e(dex_file, mid); + + size_t string_hash = std::hash()(std::string_view(e.name, e.name_len)); + size_t sig_hash = std::hash()(e.signature.ToString()); + e.cached_hash = BaseHashType::HashCombine(BaseHashType::HashCombine(0u, string_hash), + sig_hash); + + auto it = map.find(e); + if (it != map.end()) { + log_fn(it->second, i); + } else { + map.insert(std::make_pair(e, i)); + } + } +} + +void CheckVTableHasNoDuplicates(Thread* self, + Handle klass, + PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_) { + switch (pointer_size) { + case PointerSize::k64: + CheckVTableHasNoDuplicates(self, klass); + break; + case PointerSize::k32: + CheckVTableHasNoDuplicates(self, klass); + break; + } +} + +static void CheckVTable(Thread* self, Handle klass, PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_) { + CheckClassOwnsVTableEntries(self, klass, pointer_size); + CheckVTableHasNoDuplicates(self, klass, pointer_size); +} + +} // namespace + +void ClassLinker::FillImtFromSuperClass(Handle klass, + ArtMethod* unimplemented_method, + ArtMethod* imt_conflict_method, + bool* new_conflict, + ArtMethod** imt) { + DCHECK(klass->HasSuperClass()); + ObjPtr super_class = klass->GetSuperClass(); + if (super_class->ShouldHaveImt()) { + ImTable* super_imt = super_class->GetImt(image_pointer_size_); + for (size_t i = 0; i < ImTable::kSize; ++i) { + imt[i] = super_imt->Get(i, image_pointer_size_); + } + } else { + // No imt in the super class, need to reconstruct from the iftable. + ObjPtr if_table = super_class->GetIfTable(); + if (if_table->Count() != 0) { + // Ignore copied methods since we will handle these in LinkInterfaceMethods. + FillIMTFromIfTable(if_table, + unimplemented_method, + imt_conflict_method, + klass.Get(), + /*create_conflict_tables=*/false, + /*ignore_copied_methods=*/true, + /*out*/new_conflict, + /*out*/imt); + } + } +} + +class ClassLinker::LinkInterfaceMethodsHelper { + public: + LinkInterfaceMethodsHelper(ClassLinker* class_linker, + Handle klass, + Thread* self, + Runtime* runtime) + : class_linker_(class_linker), + klass_(klass), + method_alignment_(ArtMethod::Alignment(class_linker->GetImagePointerSize())), + method_size_(ArtMethod::Size(class_linker->GetImagePointerSize())), + self_(self), + stack_(runtime->GetLinearAlloc()->GetArenaPool()), + allocator_(&stack_), + default_conflict_methods_(allocator_.Adapter()), + overriding_default_conflict_methods_(allocator_.Adapter()), + miranda_methods_(allocator_.Adapter()), + default_methods_(allocator_.Adapter()), + overriding_default_methods_(allocator_.Adapter()), + move_table_(allocator_.Adapter()) { + } + + ArtMethod* FindMethod(ArtMethod* interface_method, + MethodNameAndSignatureComparator& interface_name_comparator, + ArtMethod* vtable_impl) + REQUIRES_SHARED(Locks::mutator_lock_); + + ArtMethod* GetOrCreateMirandaMethod(ArtMethod* interface_method, + MethodNameAndSignatureComparator& interface_name_comparator) + REQUIRES_SHARED(Locks::mutator_lock_); + + bool HasNewVirtuals() const { + return !(miranda_methods_.empty() && + default_methods_.empty() && + overriding_default_methods_.empty() && + overriding_default_conflict_methods_.empty() && + default_conflict_methods_.empty()); + } + + void ReallocMethods() REQUIRES_SHARED(Locks::mutator_lock_); + + ObjPtr UpdateVtable( + const HashMap& default_translations, + Handle old_vtable) REQUIRES_SHARED(Locks::mutator_lock_); + + void UpdateIfTable(Handle iftable) REQUIRES_SHARED(Locks::mutator_lock_); + + void UpdateIMT(ArtMethod** out_imt); + + void CheckNoStaleMethodsInDexCache() REQUIRES_SHARED(Locks::mutator_lock_) { + if (kIsDebugBuild) { + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + // Check that there are no stale methods are in the dex cache array. + auto* resolved_methods = klass_->GetDexCache()->GetResolvedMethods(); + for (size_t i = 0, count = klass_->GetDexCache()->NumResolvedMethods(); i < count; ++i) { + auto pair = mirror::DexCache::GetNativePair(resolved_methods, i); + ArtMethod* m = pair.object; + CHECK(move_table_.find(m) == move_table_.end() || + // The original versions of copied methods will still be present so allow those too. + // Note that if the first check passes this might fail to GetDeclaringClass(). + std::find_if(m->GetDeclaringClass()->GetMethods(pointer_size).begin(), + m->GetDeclaringClass()->GetMethods(pointer_size).end(), + [m] (ArtMethod& meth) { + return &meth == m; + }) != m->GetDeclaringClass()->GetMethods(pointer_size).end()) + << "Obsolete method " << m->PrettyMethod() << " is in dex cache!"; + } + } + } + + void ClobberOldMethods(LengthPrefixedArray* old_methods, + LengthPrefixedArray* methods) { + if (kIsDebugBuild) { + CHECK(methods != nullptr); + // Put some random garbage in old methods to help find stale pointers. + if (methods != old_methods && old_methods != nullptr) { + // Need to make sure the GC is not running since it could be scanning the methods we are + // about to overwrite. + ScopedThreadStateChange tsc(self_, kSuspended); + gc::ScopedGCCriticalSection gcs(self_, + gc::kGcCauseClassLinker, + gc::kCollectorTypeClassLinker); + const size_t old_size = LengthPrefixedArray::ComputeSize(old_methods->size(), + method_size_, + method_alignment_); + memset(old_methods, 0xFEu, old_size); + } + } + } + + private: + size_t NumberOfNewVirtuals() const { + return miranda_methods_.size() + + default_methods_.size() + + overriding_default_conflict_methods_.size() + + overriding_default_methods_.size() + + default_conflict_methods_.size(); + } + + bool FillTables() REQUIRES_SHARED(Locks::mutator_lock_) { + return !klass_->IsInterface(); + } + + void LogNewVirtuals() const REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!klass_->IsInterface() || (default_methods_.empty() && miranda_methods_.empty())) + << "Interfaces should only have default-conflict methods appended to them."; + VLOG(class_linker) << mirror::Class::PrettyClass(klass_.Get()) << ": miranda_methods=" + << miranda_methods_.size() + << " default_methods=" << default_methods_.size() + << " overriding_default_methods=" << overriding_default_methods_.size() + << " default_conflict_methods=" << default_conflict_methods_.size() + << " overriding_default_conflict_methods=" + << overriding_default_conflict_methods_.size(); + } + + ClassLinker* class_linker_; + Handle klass_; + size_t method_alignment_; + size_t method_size_; + Thread* const self_; + + // These are allocated on the heap to begin, we then transfer to linear alloc when we re-create + // the virtual methods array. + // Need to use low 4GB arenas for compiler or else the pointers wont fit in 32 bit method array + // during cross compilation. + // Use the linear alloc pool since this one is in the low 4gb for the compiler. + ArenaStack stack_; + ScopedArenaAllocator allocator_; + + ScopedArenaVector default_conflict_methods_; + ScopedArenaVector overriding_default_conflict_methods_; + ScopedArenaVector miranda_methods_; + ScopedArenaVector default_methods_; + ScopedArenaVector overriding_default_methods_; + + ScopedArenaUnorderedMap move_table_; +}; + +ArtMethod* ClassLinker::LinkInterfaceMethodsHelper::FindMethod( + ArtMethod* interface_method, + MethodNameAndSignatureComparator& interface_name_comparator, + ArtMethod* vtable_impl) { + ArtMethod* current_method = nullptr; + switch (class_linker_->FindDefaultMethodImplementation(self_, + interface_method, + klass_, + /*out*/¤t_method)) { + case DefaultMethodSearchResult::kDefaultConflict: { + // Default method conflict. + DCHECK(current_method == nullptr); + ArtMethod* default_conflict_method = nullptr; + if (vtable_impl != nullptr && vtable_impl->IsDefaultConflicting()) { + // We can reuse the method from the superclass, don't bother adding it to virtuals. + default_conflict_method = vtable_impl; + } else { + // See if we already have a conflict method for this method. + ArtMethod* preexisting_conflict = FindSameNameAndSignature( + interface_name_comparator, + default_conflict_methods_, + overriding_default_conflict_methods_); + if (LIKELY(preexisting_conflict != nullptr)) { + // We already have another conflict we can reuse. + default_conflict_method = preexisting_conflict; + } else { + // Note that we do this even if we are an interface since we need to create this and + // cannot reuse another classes. + // Create a new conflict method for this to use. + default_conflict_method = reinterpret_cast(allocator_.Alloc(method_size_)); + new(default_conflict_method) ArtMethod(interface_method, + class_linker_->GetImagePointerSize()); + if (vtable_impl == nullptr) { + // Save the conflict method. We need to add it to the vtable. + default_conflict_methods_.push_back(default_conflict_method); + } else { + // Save the conflict method but it is already in the vtable. + overriding_default_conflict_methods_.push_back(default_conflict_method); + } + } + } + current_method = default_conflict_method; + break; + } // case kDefaultConflict + case DefaultMethodSearchResult::kDefaultFound: { + DCHECK(current_method != nullptr); + // Found a default method. + if (vtable_impl != nullptr && + current_method->GetDeclaringClass() == vtable_impl->GetDeclaringClass()) { + // We found a default method but it was the same one we already have from our + // superclass. Don't bother adding it to our vtable again. + current_method = vtable_impl; + } else if (LIKELY(FillTables())) { + // Interfaces don't need to copy default methods since they don't have vtables. + // Only record this default method if it is new to save space. + // TODO It might be worthwhile to copy default methods on interfaces anyway since it + // would make lookup for interface super much faster. (We would only need to scan + // the iftable to find if there is a NSME or AME.) + ArtMethod* old = FindSameNameAndSignature(interface_name_comparator, + default_methods_, + overriding_default_methods_); + if (old == nullptr) { + // We found a default method implementation and there were no conflicts. + if (vtable_impl == nullptr) { + // Save the default method. We need to add it to the vtable. + default_methods_.push_back(current_method); + } else { + // Save the default method but it is already in the vtable. + overriding_default_methods_.push_back(current_method); + } + } else { + CHECK(old == current_method) << "Multiple default implementations selected!"; + } + } + break; + } // case kDefaultFound + case DefaultMethodSearchResult::kAbstractFound: { + DCHECK(current_method == nullptr); + // Abstract method masks all defaults. + if (vtable_impl != nullptr && + vtable_impl->IsAbstract() && + !vtable_impl->IsDefaultConflicting()) { + // We need to make this an abstract method but the version in the vtable already is so + // don't do anything. + current_method = vtable_impl; + } + break; + } // case kAbstractFound + } + return current_method; +} + +ArtMethod* ClassLinker::LinkInterfaceMethodsHelper::GetOrCreateMirandaMethod( + ArtMethod* interface_method, + MethodNameAndSignatureComparator& interface_name_comparator) { + // Find out if there is already a miranda method we can use. + ArtMethod* miranda_method = FindSameNameAndSignature(interface_name_comparator, + miranda_methods_); + if (miranda_method == nullptr) { + DCHECK(interface_method->IsAbstract()) << interface_method->PrettyMethod(); + miranda_method = reinterpret_cast(allocator_.Alloc(method_size_)); + CHECK(miranda_method != nullptr); + // Point the interface table at a phantom slot. + new(miranda_method) ArtMethod(interface_method, class_linker_->GetImagePointerSize()); + miranda_methods_.push_back(miranda_method); + } + return miranda_method; +} + +void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { + LogNewVirtuals(); + + const size_t old_method_count = klass_->NumMethods(); + const size_t new_method_count = old_method_count + NumberOfNewVirtuals(); + DCHECK_NE(old_method_count, new_method_count); + + // Attempt to realloc to save RAM if possible. + LengthPrefixedArray* old_methods = klass_->GetMethodsPtr(); + // The Realloced virtual methods aren't visible from the class roots, so there is no issue + // where GCs could attempt to mark stale pointers due to memcpy. And since we overwrite the + // realloced memory with out->CopyFrom, we are guaranteed to have objects in the to space since + // CopyFrom has internal read barriers. + // + // TODO We should maybe move some of this into mirror::Class or at least into another method. + const size_t old_size = LengthPrefixedArray::ComputeSize(old_method_count, + method_size_, + method_alignment_); + const size_t new_size = LengthPrefixedArray::ComputeSize(new_method_count, + method_size_, + method_alignment_); + const size_t old_methods_ptr_size = (old_methods != nullptr) ? old_size : 0; + auto* methods = reinterpret_cast*>( + class_linker_->GetAllocatorForClassLoader(klass_->GetClassLoader())->Realloc( + self_, old_methods, old_methods_ptr_size, new_size)); + CHECK(methods != nullptr); // Native allocation failure aborts. + + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + if (methods != old_methods) { + // Maps from heap allocated miranda method to linear alloc miranda method. + StrideIterator out = methods->begin(method_size_, method_alignment_); + // Copy over the old methods. + for (auto& m : klass_->GetMethods(pointer_size)) { + move_table_.emplace(&m, &*out); + // The CopyFrom is only necessary to not miss read barriers since Realloc won't do read + // barriers when it copies. + out->CopyFrom(&m, pointer_size); + ++out; + } + } + StrideIterator out(methods->begin(method_size_, method_alignment_) + old_method_count); + // Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and + // we want the roots of the miranda methods to get visited. + for (size_t i = 0; i < miranda_methods_.size(); ++i) { + ArtMethod* mir_method = miranda_methods_[i]; + ArtMethod& new_method = *out; + new_method.CopyFrom(mir_method, pointer_size); + uint32_t access_flags = new_method.GetAccessFlags(); + DCHECK_EQ(access_flags & kAccIntrinsic, 0u) << "Miranda method should not be an intrinsic!"; + DCHECK_EQ(access_flags & kAccDefault, 0u) << "Miranda method should not be a default method!"; + DCHECK_NE(access_flags & kAccAbstract, 0u) << "Miranda method should be abstract!"; + new_method.SetAccessFlags(access_flags | kAccCopied); + move_table_.emplace(mir_method, &new_method); + // Update the entry in the method array, as the array will be used for future lookups, + // where thread suspension is allowed. + // As such, the array should not contain locally allocated ArtMethod, otherwise the GC + // would not see them. + miranda_methods_[i] = &new_method; + ++out; + } + // We need to copy the default methods into our own method table since the runtime requires that + // every method on a class's vtable be in that respective class's virtual method table. + // NOTE This means that two classes might have the same implementation of a method from the same + // interface but will have different ArtMethod*s for them. This also means we cannot compare a + // default method found on a class with one found on the declaring interface directly and must + // look at the declaring class to determine if they are the same. + for (ScopedArenaVector* methods_vec : {&default_methods_, + &overriding_default_methods_}) { + for (size_t i = 0; i < methods_vec->size(); ++i) { + ArtMethod* def_method = (*methods_vec)[i]; + ArtMethod& new_method = *out; + new_method.CopyFrom(def_method, pointer_size); + // Clear the kAccSkipAccessChecks flag if it is present. Since this class hasn't been + // verified yet it shouldn't have methods that are skipping access checks. + // TODO This is rather arbitrary. We should maybe support classes where only some of its + // methods are skip_access_checks. + DCHECK_EQ(new_method.GetAccessFlags() & kAccNative, 0u); + constexpr uint32_t kSetFlags = kAccDefault | kAccCopied; + constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks; + new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); + move_table_.emplace(def_method, &new_method); + // Update the entry in the method array, as the array will be used for future lookups, + // where thread suspension is allowed. + // As such, the array should not contain locally allocated ArtMethod, otherwise the GC + // would not see them. + (*methods_vec)[i] = &new_method; + ++out; + } + } + for (ScopedArenaVector* methods_vec : {&default_conflict_methods_, + &overriding_default_conflict_methods_}) { + for (size_t i = 0; i < methods_vec->size(); ++i) { + ArtMethod* conf_method = (*methods_vec)[i]; + ArtMethod& new_method = *out; + new_method.CopyFrom(conf_method, pointer_size); + // This is a type of default method (there are default method impls, just a conflict) so + // mark this as a default. We use the `kAccAbstract` flag to distinguish it from invokable + // copied default method without using a separate access flag but the default conflicting + // method is technically not abstract and ArtMethod::IsAbstract() shall return false. + // Also clear the kAccSkipAccessChecks bit since this class hasn't been verified yet it + // shouldn't have methods that are skipping access checks. Also clear potential + // kAccSingleImplementation to avoid CHA trying to inline the default method. + uint32_t access_flags = new_method.GetAccessFlags(); + DCHECK_EQ(access_flags & kAccNative, 0u); + DCHECK_EQ(access_flags & kAccIntrinsic, 0u); + constexpr uint32_t kSetFlags = kAccDefault | kAccAbstract | kAccCopied; + constexpr uint32_t kMaskFlags = ~(kAccSkipAccessChecks | kAccSingleImplementation); + new_method.SetAccessFlags((access_flags | kSetFlags) & kMaskFlags); + DCHECK(new_method.IsDefaultConflicting()); + DCHECK(!new_method.IsAbstract()); + // The actual method might or might not be marked abstract since we just copied it from a + // (possibly default) interface method. We need to set it entry point to be the bridge so + // that the compiler will not invoke the implementation of whatever method we copied from. + EnsureThrowsInvocationError(class_linker_, &new_method); + move_table_.emplace(conf_method, &new_method); + // Update the entry in the method array, as the array will be used for future lookups, + // where thread suspension is allowed. + // As such, the array should not contain locally allocated ArtMethod, otherwise the GC + // would not see them. + (*methods_vec)[i] = &new_method; + ++out; + } + } + methods->SetSize(new_method_count); + class_linker_->UpdateClassMethods(klass_.Get(), methods); +} + +ObjPtr ClassLinker::LinkInterfaceMethodsHelper::UpdateVtable( + const HashMap& default_translations, + Handle old_vtable) { + // Update the vtable to the new method structures. We can skip this for interfaces since they + // do not have vtables. + const size_t old_vtable_count = old_vtable->GetLength(); + const size_t new_vtable_count = old_vtable_count + + miranda_methods_.size() + + default_methods_.size() + + default_conflict_methods_.size(); + + ObjPtr vtable = ObjPtr::DownCast( + mirror::Array::CopyOf(old_vtable, self_, new_vtable_count)); + if (UNLIKELY(vtable == nullptr)) { + self_->AssertPendingOOMException(); + return nullptr; + } + + size_t vtable_pos = old_vtable_count; + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + // Update all the newly copied method's indexes so they denote their placement in the vtable. + for (const ScopedArenaVector& methods_vec : {default_methods_, + default_conflict_methods_, + miranda_methods_}) { + // These are the functions that are not already in the vtable! + for (ArtMethod* new_vtable_method : methods_vec) { + // Leave the declaring class alone the method's dex_code_item_offset_ and dex_method_index_ + // fields are references into the dex file the method was defined in. Since the ArtMethod + // does not store that information it uses declaring_class_->dex_cache_. + new_vtable_method->SetMethodIndex(0xFFFF & vtable_pos); + vtable->SetElementPtrSize(vtable_pos, new_vtable_method, pointer_size); + ++vtable_pos; + } + } + DCHECK_EQ(vtable_pos, new_vtable_count); + + // Update old vtable methods. We use the default_translations map to figure out what each + // vtable entry should be updated to, if they need to be at all. + for (size_t i = 0; i < old_vtable_count; ++i) { + ArtMethod* translated_method = vtable->GetElementPtrSize(i, pointer_size); + // Try and find what we need to change this method to. + auto translation_it = default_translations.find(i); + if (translation_it != default_translations.end()) { + if (translation_it->second.IsInConflict()) { + // Find which conflict method we are to use for this method. + MethodNameAndSignatureComparator old_method_comparator( + translated_method->GetInterfaceMethodIfProxy(pointer_size)); + // We only need to look through overriding_default_conflict_methods since this is an + // overridden method we are fixing up here. + ArtMethod* new_conflict_method = FindSameNameAndSignature( + old_method_comparator, overriding_default_conflict_methods_); + CHECK(new_conflict_method != nullptr) << "Expected a conflict method!"; + translated_method = new_conflict_method; + } else if (translation_it->second.IsAbstract()) { + // Find which miranda method we are to use for this method. + MethodNameAndSignatureComparator old_method_comparator( + translated_method->GetInterfaceMethodIfProxy(pointer_size)); + ArtMethod* miranda_method = FindSameNameAndSignature(old_method_comparator, + miranda_methods_); + DCHECK(miranda_method != nullptr); + translated_method = miranda_method; + } else { + // Normal default method (changed from an older default or abstract interface method). + DCHECK(translation_it->second.IsTranslation()); + translated_method = translation_it->second.GetTranslation(); + auto it = move_table_.find(translated_method); + DCHECK(it != move_table_.end()); + translated_method = it->second; + } + } else { + auto it = move_table_.find(translated_method); + translated_method = (it != move_table_.end()) ? it->second : nullptr; + } + + if (translated_method != nullptr) { + // Make sure the new_methods index is set. + if (translated_method->GetMethodIndexDuringLinking() != i) { + if (kIsDebugBuild) { + auto* methods = klass_->GetMethodsPtr(); + CHECK_LE(reinterpret_cast(&*methods->begin(method_size_, method_alignment_)), + reinterpret_cast(translated_method)); + CHECK_LT(reinterpret_cast(translated_method), + reinterpret_cast(&*methods->end(method_size_, method_alignment_))); + } + translated_method->SetMethodIndex(0xFFFF & i); + } + vtable->SetElementPtrSize(i, translated_method, pointer_size); + } + } + klass_->SetVTable(vtable); + return vtable; +} + +void ClassLinker::LinkInterfaceMethodsHelper::UpdateIfTable(Handle iftable) { + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + const size_t ifcount = klass_->GetIfTableCount(); + // Go fix up all the stale iftable pointers. + for (size_t i = 0; i < ifcount; ++i) { + for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) { + ObjPtr method_array = iftable->GetMethodArray(i); + ArtMethod* m = method_array->GetElementPtrSize(j, pointer_size); + DCHECK(m != nullptr) << klass_->PrettyClass(); + auto it = move_table_.find(m); + if (it != move_table_.end()) { + auto* new_m = it->second; + DCHECK(new_m != nullptr) << klass_->PrettyClass(); + method_array->SetElementPtrSize(j, new_m, pointer_size); + } + } + } +} + +void ClassLinker::LinkInterfaceMethodsHelper::UpdateIMT(ArtMethod** out_imt) { + // Fix up IMT next. + for (size_t i = 0; i < ImTable::kSize; ++i) { + auto it = move_table_.find(out_imt[i]); + if (it != move_table_.end()) { + out_imt[i] = it->second; + } + } +} + +// TODO This method needs to be split up into several smaller methods. +bool ClassLinker::LinkInterfaceMethods( + Thread* self, + Handle klass, + const HashMap& default_translations, + bool* out_new_conflict, + ArtMethod** out_imt) { + StackHandleScope<3> hs(self); + Runtime* const runtime = Runtime::Current(); + + const bool is_interface = klass->IsInterface(); + const bool has_superclass = klass->HasSuperClass(); + const bool fill_tables = !is_interface; + const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U; + const size_t ifcount = klass->GetIfTableCount(); + + Handle iftable(hs.NewHandle(klass->GetIfTable())); + + MutableHandle vtable(hs.NewHandle(klass->GetVTableDuringLinking())); + ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod(); + ArtMethod* const imt_conflict_method = runtime->GetImtConflictMethod(); + // Copy the IMT from the super class if possible. + const bool extend_super_iftable = has_superclass; + if (has_superclass && fill_tables) { + FillImtFromSuperClass(klass, + unimplemented_method, + imt_conflict_method, + out_new_conflict, + out_imt); + } + // Allocate method arrays before since we don't want miss visiting miranda method roots due to + // thread suspension. + if (fill_tables) { + if (!AllocateIfTableMethodArrays(self, klass, iftable)) { + return false; + } + } + + LinkInterfaceMethodsHelper helper(this, klass, self, runtime); + + auto* old_cause = self->StartAssertNoThreadSuspension( + "Copying ArtMethods for LinkInterfaceMethods"); + // Going in reverse to ensure that we will hit abstract methods that override defaults before the + // defaults. This means we don't need to do any trickery when creating the Miranda methods, since + // they will already be null. This has the additional benefit that the declarer of a miranda + // method will actually declare an abstract method. + for (size_t i = ifcount; i != 0u; ) { + --i; + DCHECK_LT(i, ifcount); + + size_t num_methods = iftable->GetInterface(i)->NumDeclaredVirtualMethods(); + if (num_methods > 0) { + StackHandleScope<2> hs2(self); + const bool is_super = i < super_ifcount; + const bool super_interface = is_super && extend_super_iftable; + // We don't actually create or fill these tables for interfaces, we just copy some methods for + // conflict methods. Just set this as nullptr in those cases. + Handle method_array(fill_tables + ? hs2.NewHandle(iftable->GetMethodArray(i)) + : hs2.NewHandle(nullptr)); + + ArraySlice input_virtual_methods; + ScopedNullHandle null_handle; + Handle input_vtable_array(null_handle); + int32_t input_array_length = 0; + + // TODO Cleanup Needed: In the presence of default methods this optimization is rather dirty + // and confusing. Default methods should always look through all the superclasses + // because they are the last choice of an implementation. We get around this by looking + // at the super-classes iftable methods (copied into method_array previously) when we are + // looking for the implementation of a super-interface method but that is rather dirty. + bool using_virtuals; + if (super_interface || is_interface) { + // If we are overwriting a super class interface, try to only virtual methods instead of the + // whole vtable. + using_virtuals = true; + input_virtual_methods = klass->GetDeclaredVirtualMethodsSlice(image_pointer_size_); + input_array_length = input_virtual_methods.size(); + } else { + // For a new interface, however, we need the whole vtable in case a new + // interface method is implemented in the whole superclass. + using_virtuals = false; + DCHECK(vtable != nullptr); + input_vtable_array = vtable; + input_array_length = input_vtable_array->GetLength(); + } + + // For each method in interface + for (size_t j = 0; j < num_methods; ++j) { + auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_); + MethodNameAndSignatureComparator interface_name_comparator( + interface_method->GetInterfaceMethodIfProxy(image_pointer_size_)); + uint32_t imt_index = interface_method->GetImtIndex(); + ArtMethod** imt_ptr = &out_imt[imt_index]; + // For each method listed in the interface's method list, find the + // matching method in our class's method list. We want to favor the + // subclass over the superclass, which just requires walking + // back from the end of the vtable. (This only matters if the + // superclass defines a private method and this class redefines + // it -- otherwise it would use the same vtable slot. In .dex files + // those don't end up in the virtual method table, so it shouldn't + // matter which direction we go. We walk it backward anyway.) + // + // To find defaults we need to do the same but also go over interfaces. + bool found_impl = false; + ArtMethod* vtable_impl = nullptr; + for (int32_t k = input_array_length - 1; k >= 0; --k) { + ArtMethod* vtable_method = using_virtuals ? + &input_virtual_methods[k] : + input_vtable_array->GetElementPtrSize(k, image_pointer_size_); + ArtMethod* vtable_method_for_name_comparison = + vtable_method->GetInterfaceMethodIfProxy(image_pointer_size_); + DCHECK(!vtable_method->IsStatic()) << vtable_method->PrettyMethod(); + if (interface_name_comparator.HasSameNameAndSignature( + vtable_method_for_name_comparison)) { + if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) { + // Must do EndAssertNoThreadSuspension before throw since the throw can cause + // allocations. + self->EndAssertNoThreadSuspension(old_cause); + ThrowIllegalAccessError(klass.Get(), + "Method '%s' implementing interface method '%s' is not public", + vtable_method->PrettyMethod().c_str(), + interface_method->PrettyMethod().c_str()); + return false; + } else if (UNLIKELY(vtable_method->IsOverridableByDefaultMethod())) { + // We might have a newer, better, default method for this, so we just skip it. If we + // are still using this we will select it again when scanning for default methods. To + // obviate the need to copy the method again we will make a note that we already found + // a default here. + // TODO This should be much cleaner. + vtable_impl = vtable_method; + break; + } else { + found_impl = true; + if (LIKELY(fill_tables)) { + method_array->SetElementPtrSize(j, vtable_method, image_pointer_size_); + // Place method in imt if entry is empty, place conflict otherwise. + SetIMTRef(unimplemented_method, + imt_conflict_method, + vtable_method, + /*out*/out_new_conflict, + /*out*/imt_ptr); + } + break; + } + } + } + // Continue on to the next method if we are done. + if (LIKELY(found_impl)) { + continue; + } else if (LIKELY(super_interface)) { + // Don't look for a default implementation when the super-method is implemented directly + // by the class. + // + // See if we can use the superclasses method and skip searching everything else. + // Note: !found_impl && super_interface + CHECK(extend_super_iftable); + // If this is a super_interface method it is possible we shouldn't override it because a + // superclass could have implemented it directly. We get the method the superclass used + // to implement this to know if we can override it with a default method. Doing this is + // safe since we know that the super_iftable is filled in so we can simply pull it from + // there. We don't bother if this is not a super-classes interface since in that case we + // have scanned the entire vtable anyway and would have found it. + // TODO This is rather dirty but it is faster than searching through the entire vtable + // every time. + ArtMethod* supers_method = + method_array->GetElementPtrSize(j, image_pointer_size_); + DCHECK(supers_method != nullptr); + DCHECK(interface_name_comparator.HasSameNameAndSignature(supers_method)); + if (LIKELY(!supers_method->IsOverridableByDefaultMethod())) { + // The method is not overridable by a default method (i.e. it is directly implemented + // in some class). Therefore move onto the next interface method. + continue; + } else { + // If the super-classes method is override-able by a default method we need to keep + // track of it since though it is override-able it is not guaranteed to be 'overridden'. + // If it turns out not to be overridden and we did not keep track of it we might add it + // to the vtable twice, causing corruption (vtable entries having inconsistent and + // illegal states, incorrect vtable size, and incorrect or inconsistent iftable entries) + // in this class and any subclasses. + DCHECK(vtable_impl == nullptr || vtable_impl == supers_method) + << "vtable_impl was " << ArtMethod::PrettyMethod(vtable_impl) + << " and not 'nullptr' or " + << supers_method->PrettyMethod() + << " as expected. IFTable appears to be corrupt!"; + vtable_impl = supers_method; + } + } + // If we haven't found it yet we should search through the interfaces for default methods. + ArtMethod* current_method = helper.FindMethod(interface_method, + interface_name_comparator, + vtable_impl); + if (LIKELY(fill_tables)) { + if (current_method == nullptr && !super_interface) { + // We could not find an implementation for this method and since it is a brand new + // interface we searched the entire vtable (and all default methods) for an + // implementation but couldn't find one. We therefore need to make a miranda method. + current_method = helper.GetOrCreateMirandaMethod(interface_method, + interface_name_comparator); + } + + if (current_method != nullptr) { + // We found a default method implementation. Record it in the iftable and IMT. + method_array->SetElementPtrSize(j, current_method, image_pointer_size_); + SetIMTRef(unimplemented_method, + imt_conflict_method, + current_method, + /*out*/out_new_conflict, + /*out*/imt_ptr); + } + } + } // For each method in interface end. + } // if (num_methods > 0) + } // For each interface. + // TODO don't extend virtuals of interface unless necessary (when is it?). + if (helper.HasNewVirtuals()) { + LengthPrefixedArray* old_methods = kIsDebugBuild ? klass->GetMethodsPtr() : nullptr; + helper.ReallocMethods(); // No return value to check. Native allocation failure aborts. + LengthPrefixedArray* methods = kIsDebugBuild ? klass->GetMethodsPtr() : nullptr; + + // Done copying methods, they are all roots in the class now, so we can end the no thread + // suspension assert. + self->EndAssertNoThreadSuspension(old_cause); + + if (fill_tables) { + vtable.Assign(helper.UpdateVtable(default_translations, vtable)); + if (UNLIKELY(vtable == nullptr)) { + // The helper has already called self->AssertPendingOOMException(); + return false; + } + helper.UpdateIfTable(iftable); + helper.UpdateIMT(out_imt); + } + + helper.CheckNoStaleMethodsInDexCache(); + helper.ClobberOldMethods(old_methods, methods); + } else { + self->EndAssertNoThreadSuspension(old_cause); + } + if (kIsDebugBuild && !is_interface) { + CheckVTable(self, klass, image_pointer_size_); + } + return true; +} + +class ClassLinker::LinkFieldsHelper { + public: + static bool LinkFields(ClassLinker* class_linker, + Thread* self, + Handle klass, + bool is_static, + size_t* class_size) + REQUIRES_SHARED(Locks::mutator_lock_); + + private: + enum class FieldTypeOrder : uint16_t; + class FieldGaps; + + struct FieldTypeOrderAndIndex { + FieldTypeOrder field_type_order; + uint16_t field_index; + }; + + static FieldTypeOrder FieldTypeOrderFromFirstDescriptorCharacter(char first_char); + + template + static MemberOffset AssignFieldOffset(ArtField* field, MemberOffset field_offset) + REQUIRES_SHARED(Locks::mutator_lock_); +}; + +// We use the following order of field types for assigning offsets. +// Some fields can be shuffled forward to fill gaps, see `ClassLinker::LinkFields()`. +enum class ClassLinker::LinkFieldsHelper::FieldTypeOrder : uint16_t { + kReference = 0u, + kLong, + kDouble, + kInt, + kFloat, + kChar, + kShort, + kBoolean, + kByte, + + kLast64BitType = kDouble, + kLast32BitType = kFloat, + kLast16BitType = kShort, +}; + +ALWAYS_INLINE +ClassLinker::LinkFieldsHelper::FieldTypeOrder +ClassLinker::LinkFieldsHelper::FieldTypeOrderFromFirstDescriptorCharacter(char first_char) { + switch (first_char) { + case 'J': + return FieldTypeOrder::kLong; + case 'D': + return FieldTypeOrder::kDouble; + case 'I': + return FieldTypeOrder::kInt; + case 'F': + return FieldTypeOrder::kFloat; + case 'C': + return FieldTypeOrder::kChar; + case 'S': + return FieldTypeOrder::kShort; + case 'Z': + return FieldTypeOrder::kBoolean; + case 'B': + return FieldTypeOrder::kByte; + default: + DCHECK(first_char == 'L' || first_char == '[') << first_char; + return FieldTypeOrder::kReference; + } +} + +// Gaps where we can insert fields in object layout. +class ClassLinker::LinkFieldsHelper::FieldGaps { + public: + template + ALWAYS_INLINE MemberOffset AlignFieldOffset(MemberOffset field_offset) { + static_assert(kSize == 2u || kSize == 4u || kSize == 8u); + if (!IsAligned(field_offset.Uint32Value())) { + uint32_t gap_start = field_offset.Uint32Value(); + field_offset = MemberOffset(RoundUp(gap_start, kSize)); + AddGaps(gap_start, field_offset.Uint32Value()); + } + return field_offset; + } + + template + bool HasGap() const { + static_assert(kSize == 1u || kSize == 2u || kSize == 4u); + return (kSize == 1u && gap1_offset_ != kNoOffset) || + (kSize <= 2u && gap2_offset_ != kNoOffset) || + gap4_offset_ != kNoOffset; + } + + template + MemberOffset ReleaseGap() { + static_assert(kSize == 1u || kSize == 2u || kSize == 4u); + uint32_t result; + if (kSize == 1u && gap1_offset_ != kNoOffset) { + DCHECK(gap2_offset_ == kNoOffset || gap2_offset_ > gap1_offset_); + DCHECK(gap4_offset_ == kNoOffset || gap4_offset_ > gap1_offset_); + result = gap1_offset_; + gap1_offset_ = kNoOffset; + } else if (kSize <= 2u && gap2_offset_ != kNoOffset) { + DCHECK(gap4_offset_ == kNoOffset || gap4_offset_ > gap2_offset_); + result = gap2_offset_; + gap2_offset_ = kNoOffset; + if (kSize < 2u) { + AddGaps<1u>(result + kSize, result + 2u); + } + } else { + DCHECK_NE(gap4_offset_, kNoOffset); + result = gap4_offset_; + gap4_offset_ = kNoOffset; + if (kSize < 4u) { + AddGaps(result + kSize, result + 4u); + } + } + return MemberOffset(result); + } + + private: + template + void AddGaps(uint32_t gap_start, uint32_t gap_end) { + if ((kGapsToCheck & 1u) != 0u) { + DCHECK_LT(gap_start, gap_end); + DCHECK_ALIGNED(gap_end, 2u); + if ((gap_start & 1u) != 0u) { + DCHECK_EQ(gap1_offset_, kNoOffset); + gap1_offset_ = gap_start; + gap_start += 1u; + if (kGapsToCheck == 1u || gap_start == gap_end) { + DCHECK_EQ(gap_start, gap_end); + return; + } + } + } + + if ((kGapsToCheck & 2u) != 0u) { + DCHECK_LT(gap_start, gap_end); + DCHECK_ALIGNED(gap_start, 2u); + DCHECK_ALIGNED(gap_end, 4u); + if ((gap_start & 2u) != 0u) { + DCHECK_EQ(gap2_offset_, kNoOffset); + gap2_offset_ = gap_start; + gap_start += 2u; + if (kGapsToCheck <= 3u || gap_start == gap_end) { + DCHECK_EQ(gap_start, gap_end); + return; + } + } + } + + if ((kGapsToCheck & 4u) != 0u) { + DCHECK_LT(gap_start, gap_end); + DCHECK_ALIGNED(gap_start, 4u); + DCHECK_ALIGNED(gap_end, 8u); + DCHECK_EQ(gap_start + 4u, gap_end); + DCHECK_EQ(gap4_offset_, kNoOffset); + gap4_offset_ = gap_start; + return; + } + + DCHECK(false) << "Remaining gap: " << gap_start << " to " << gap_end + << " after checking " << kGapsToCheck; + } + + static constexpr uint32_t kNoOffset = static_cast(-1); + + uint32_t gap4_offset_ = kNoOffset; + uint32_t gap2_offset_ = kNoOffset; + uint32_t gap1_offset_ = kNoOffset; +}; + +template +ALWAYS_INLINE +MemberOffset ClassLinker::LinkFieldsHelper::AssignFieldOffset(ArtField* field, + MemberOffset field_offset) { + DCHECK_ALIGNED(field_offset.Uint32Value(), kSize); + DCHECK_EQ(Primitive::ComponentSize(field->GetTypeAsPrimitiveType()), kSize); + field->SetOffset(field_offset); + return MemberOffset(field_offset.Uint32Value() + kSize); +} + +bool ClassLinker::LinkFieldsHelper::LinkFields(ClassLinker* class_linker, + Thread* self, + Handle klass, + bool is_static, + size_t* class_size) { + self->AllowThreadSuspension(); + const size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields(); + LengthPrefixedArray* const fields = is_static ? klass->GetSFieldsPtr() : + klass->GetIFieldsPtr(); + + // Initialize field_offset + MemberOffset field_offset(0); + if (is_static) { + field_offset = klass->GetFirstReferenceStaticFieldOffsetDuringLinking( + class_linker->GetImagePointerSize()); + } else { + ObjPtr super_class = klass->GetSuperClass(); + if (super_class != nullptr) { + CHECK(super_class->IsResolved()) + << klass->PrettyClass() << " " << super_class->PrettyClass(); + field_offset = MemberOffset(super_class->GetObjectSize()); + } + } + + CHECK_EQ(num_fields == 0, fields == nullptr) << klass->PrettyClass(); + + // we want a relatively stable order so that adding new fields + // minimizes disruption of C++ version such as Class and Method. + // + // The overall sort order order is: + // 1) All object reference fields, sorted alphabetically. + // 2) All java long (64-bit) integer fields, sorted alphabetically. + // 3) All java double (64-bit) floating point fields, sorted alphabetically. + // 4) All java int (32-bit) integer fields, sorted alphabetically. + // 5) All java float (32-bit) floating point fields, sorted alphabetically. + // 6) All java char (16-bit) integer fields, sorted alphabetically. + // 7) All java short (16-bit) integer fields, sorted alphabetically. + // 8) All java boolean (8-bit) integer fields, sorted alphabetically. + // 9) All java byte (8-bit) integer fields, sorted alphabetically. + // + // (References are first to increase the chance of reference visiting + // being able to take a fast path using a bitmap of references at the + // start of the object, see `Class::reference_instance_offsets_`.) + // + // Once the fields are sorted in this order we will attempt to fill any gaps + // that might be present in the memory layout of the structure. + // Note that we shall not fill gaps between the superclass fields. + + // Collect fields and their "type order index" (see numbered points above). + const char* old_no_suspend_cause = self->StartAssertNoThreadSuspension( + "Using plain ArtField references"); + constexpr size_t kStackBufferEntries = 64; // Avoid allocations for small number of fields. + FieldTypeOrderAndIndex stack_buffer[kStackBufferEntries]; + std::vector heap_buffer; + ArrayRef sorted_fields; + if (num_fields <= kStackBufferEntries) { + sorted_fields = ArrayRef(stack_buffer, num_fields); + } else { + heap_buffer.resize(num_fields); + sorted_fields = ArrayRef(heap_buffer); + } + size_t num_reference_fields = 0; + size_t primitive_fields_start = num_fields; + DCHECK_LE(num_fields, 1u << 16); + for (size_t i = 0; i != num_fields; ++i) { + ArtField* field = &fields->At(i); + const char* descriptor = field->GetTypeDescriptor(); + FieldTypeOrder field_type_order = FieldTypeOrderFromFirstDescriptorCharacter(descriptor[0]); + uint16_t field_index = dchecked_integral_cast(i); + // Insert references to the start, other fields to the end. + DCHECK_LT(num_reference_fields, primitive_fields_start); + if (field_type_order == FieldTypeOrder::kReference) { + sorted_fields[num_reference_fields] = { field_type_order, field_index }; + ++num_reference_fields; + } else { + --primitive_fields_start; + sorted_fields[primitive_fields_start] = { field_type_order, field_index }; + } + } + DCHECK_EQ(num_reference_fields, primitive_fields_start); + + // Reference fields are already sorted by field index (and dex field index). + DCHECK(std::is_sorted( + sorted_fields.begin(), + sorted_fields.begin() + num_reference_fields, + [fields](const auto& lhs, const auto& rhs) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtField* lhs_field = &fields->At(lhs.field_index); + ArtField* rhs_field = &fields->At(rhs.field_index); + CHECK_EQ(lhs_field->GetTypeAsPrimitiveType(), Primitive::kPrimNot); + CHECK_EQ(rhs_field->GetTypeAsPrimitiveType(), Primitive::kPrimNot); + CHECK_EQ(lhs_field->GetDexFieldIndex() < rhs_field->GetDexFieldIndex(), + lhs.field_index < rhs.field_index); + return lhs_field->GetDexFieldIndex() < rhs_field->GetDexFieldIndex(); + })); + // Primitive fields were stored in reverse order of their field index (and dex field index). + DCHECK(std::is_sorted( + sorted_fields.begin() + primitive_fields_start, + sorted_fields.end(), + [fields](const auto& lhs, const auto& rhs) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtField* lhs_field = &fields->At(lhs.field_index); + ArtField* rhs_field = &fields->At(rhs.field_index); + CHECK_NE(lhs_field->GetTypeAsPrimitiveType(), Primitive::kPrimNot); + CHECK_NE(rhs_field->GetTypeAsPrimitiveType(), Primitive::kPrimNot); + CHECK_EQ(lhs_field->GetDexFieldIndex() > rhs_field->GetDexFieldIndex(), + lhs.field_index > rhs.field_index); + return lhs.field_index > rhs.field_index; + })); + // Sort the primitive fields by the field type order, then field index. + std::sort(sorted_fields.begin() + primitive_fields_start, + sorted_fields.end(), + [](const auto& lhs, const auto& rhs) { + if (lhs.field_type_order != rhs.field_type_order) { + return lhs.field_type_order < rhs.field_type_order; + } else { + return lhs.field_index < rhs.field_index; + } + }); + // Primitive fields are now sorted by field size (descending), then type, then field index. + DCHECK(std::is_sorted( + sorted_fields.begin() + primitive_fields_start, + sorted_fields.end(), + [fields](const auto& lhs, const auto& rhs) REQUIRES_SHARED(Locks::mutator_lock_) { + ArtField* lhs_field = &fields->At(lhs.field_index); + ArtField* rhs_field = &fields->At(rhs.field_index); + Primitive::Type lhs_type = lhs_field->GetTypeAsPrimitiveType(); + CHECK_NE(lhs_type, Primitive::kPrimNot); + Primitive::Type rhs_type = rhs_field->GetTypeAsPrimitiveType(); + CHECK_NE(rhs_type, Primitive::kPrimNot); + if (lhs_type != rhs_type) { + size_t lhs_size = Primitive::ComponentSize(lhs_type); + size_t rhs_size = Primitive::ComponentSize(rhs_type); + return (lhs_size != rhs_size) ? (lhs_size > rhs_size) : (lhs_type < rhs_type); + } else { + return lhs_field->GetDexFieldIndex() < rhs_field->GetDexFieldIndex(); + } + })); + + // Process reference fields. + FieldGaps field_gaps; + size_t index = 0u; + if (num_reference_fields != 0u) { + constexpr size_t kReferenceSize = sizeof(mirror::HeapReference); + field_offset = field_gaps.AlignFieldOffset(field_offset); + for (; index != num_reference_fields; ++index) { + ArtField* field = &fields->At(sorted_fields[index].field_index); + field_offset = AssignFieldOffset(field, field_offset); + } + } + // Process 64-bit fields. + if (index != num_fields && + sorted_fields[index].field_type_order <= FieldTypeOrder::kLast64BitType) { + field_offset = field_gaps.AlignFieldOffset<8u>(field_offset); + while (index != num_fields && + sorted_fields[index].field_type_order <= FieldTypeOrder::kLast64BitType) { + ArtField* field = &fields->At(sorted_fields[index].field_index); + field_offset = AssignFieldOffset<8u>(field, field_offset); + ++index; + } + } + // Process 32-bit fields. + if (index != num_fields && + sorted_fields[index].field_type_order <= FieldTypeOrder::kLast32BitType) { + field_offset = field_gaps.AlignFieldOffset<4u>(field_offset); + if (field_gaps.HasGap<4u>()) { + ArtField* field = &fields->At(sorted_fields[index].field_index); + AssignFieldOffset<4u>(field, field_gaps.ReleaseGap<4u>()); // Ignore return value. + ++index; + DCHECK(!field_gaps.HasGap<4u>()); // There can be only one gap for a 32-bit field. + } + while (index != num_fields && + sorted_fields[index].field_type_order <= FieldTypeOrder::kLast32BitType) { + ArtField* field = &fields->At(sorted_fields[index].field_index); + field_offset = AssignFieldOffset<4u>(field, field_offset); + ++index; + } + } + // Process 16-bit fields. + if (index != num_fields && + sorted_fields[index].field_type_order <= FieldTypeOrder::kLast16BitType) { + field_offset = field_gaps.AlignFieldOffset<2u>(field_offset); + while (index != num_fields && + sorted_fields[index].field_type_order <= FieldTypeOrder::kLast16BitType && + field_gaps.HasGap<2u>()) { + ArtField* field = &fields->At(sorted_fields[index].field_index); + AssignFieldOffset<2u>(field, field_gaps.ReleaseGap<2u>()); // Ignore return value. + ++index; + } + while (index != num_fields && + sorted_fields[index].field_type_order <= FieldTypeOrder::kLast16BitType) { + ArtField* field = &fields->At(sorted_fields[index].field_index); + field_offset = AssignFieldOffset<2u>(field, field_offset); + ++index; + } + } + // Process 8-bit fields. + for (; index != num_fields && field_gaps.HasGap<1u>(); ++index) { + ArtField* field = &fields->At(sorted_fields[index].field_index); + AssignFieldOffset<1u>(field, field_gaps.ReleaseGap<1u>()); // Ignore return value. + } + for (; index != num_fields; ++index) { + ArtField* field = &fields->At(sorted_fields[index].field_index); + field_offset = AssignFieldOffset<1u>(field, field_offset); + } + + self->EndAssertNoThreadSuspension(old_no_suspend_cause); + + // We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it. + DCHECK(!class_linker->init_done_ || !klass->DescriptorEquals("Ljava/lang/ref/Reference;")); + if (!is_static && + UNLIKELY(!class_linker->init_done_) && + klass->DescriptorEquals("Ljava/lang/ref/Reference;")) { + // We know there are no non-reference fields in the Reference classes, and we know + // that 'referent' is alphabetically last, so this is easy... + CHECK_EQ(num_reference_fields, num_fields) << klass->PrettyClass(); + CHECK_STREQ(fields->At(num_fields - 1).GetName(), "referent") + << klass->PrettyClass(); + --num_reference_fields; + } + + size_t size = field_offset.Uint32Value(); + // Update klass + if (is_static) { + klass->SetNumReferenceStaticFields(num_reference_fields); + *class_size = size; + } else { + klass->SetNumReferenceInstanceFields(num_reference_fields); + ObjPtr super_class = klass->GetSuperClass(); + if (num_reference_fields == 0 || super_class == nullptr) { + // object has one reference field, klass, but we ignore it since we always visit the class. + // super_class is null iff the class is java.lang.Object. + if (super_class == nullptr || + (super_class->GetClassFlags() & mirror::kClassFlagNoReferenceFields) != 0) { + klass->SetClassFlags(klass->GetClassFlags() | mirror::kClassFlagNoReferenceFields); + } + } + if (kIsDebugBuild) { + DCHECK_EQ(super_class == nullptr, klass->DescriptorEquals("Ljava/lang/Object;")); + size_t total_reference_instance_fields = 0; + ObjPtr cur_super = klass.Get(); + while (cur_super != nullptr) { + total_reference_instance_fields += cur_super->NumReferenceInstanceFieldsDuringLinking(); + cur_super = cur_super->GetSuperClass(); + } + if (super_class == nullptr) { + CHECK_EQ(total_reference_instance_fields, 1u) << klass->PrettyDescriptor(); + } else { + // Check that there is at least num_reference_fields other than Object.class. + CHECK_GE(total_reference_instance_fields, 1u + num_reference_fields) + << klass->PrettyClass(); + } + } + if (!klass->IsVariableSize()) { + std::string temp; + DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp); + size_t previous_size = klass->GetObjectSize(); + if (previous_size != 0) { + // Make sure that we didn't originally have an incorrect size. + CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp); + } + klass->SetObjectSize(size); + } + } + + if (kIsDebugBuild) { + // Make sure that the fields array is ordered by name but all reference + // offsets are at the beginning as far as alignment allows. + MemberOffset start_ref_offset = is_static + ? klass->GetFirstReferenceStaticFieldOffsetDuringLinking(class_linker->image_pointer_size_) + : klass->GetFirstReferenceInstanceFieldOffset(); + MemberOffset end_ref_offset(start_ref_offset.Uint32Value() + + num_reference_fields * + sizeof(mirror::HeapReference)); + MemberOffset current_ref_offset = start_ref_offset; + for (size_t i = 0; i < num_fields; i++) { + ArtField* field = &fields->At(i); + VLOG(class_linker) << "LinkFields: " << (is_static ? "static" : "instance") + << " class=" << klass->PrettyClass() << " field=" << field->PrettyField() + << " offset=" << field->GetOffsetDuringLinking(); + if (i != 0) { + ArtField* const prev_field = &fields->At(i - 1); + // NOTE: The field names can be the same. This is not possible in the Java language + // but it's valid Java/dex bytecode and for example proguard can generate such bytecode. + DCHECK_LE(strcmp(prev_field->GetName(), field->GetName()), 0); + } + Primitive::Type type = field->GetTypeAsPrimitiveType(); + bool is_primitive = type != Primitive::kPrimNot; + if (klass->DescriptorEquals("Ljava/lang/ref/Reference;") && + strcmp("referent", field->GetName()) == 0) { + is_primitive = true; // We lied above, so we have to expect a lie here. + } + MemberOffset offset = field->GetOffsetDuringLinking(); + if (is_primitive) { + if (offset.Uint32Value() < end_ref_offset.Uint32Value()) { + // Shuffled before references. + size_t type_size = Primitive::ComponentSize(type); + CHECK_LT(type_size, sizeof(mirror::HeapReference)); + CHECK_LT(offset.Uint32Value(), start_ref_offset.Uint32Value()); + CHECK_LE(offset.Uint32Value() + type_size, start_ref_offset.Uint32Value()); + CHECK(!IsAligned)>(offset.Uint32Value())); + } + } else { + CHECK_EQ(current_ref_offset.Uint32Value(), offset.Uint32Value()); + current_ref_offset = MemberOffset(current_ref_offset.Uint32Value() + + sizeof(mirror::HeapReference)); + } + } + CHECK_EQ(current_ref_offset.Uint32Value(), end_ref_offset.Uint32Value()); + } + return true; +} + +bool ClassLinker::LinkInstanceFields(Thread* self, Handle klass) { + CHECK(klass != nullptr); + return LinkFieldsHelper::LinkFields(this, self, klass, false, nullptr); +} + +bool ClassLinker::LinkStaticFields(Thread* self, Handle klass, size_t* class_size) { + CHECK(klass != nullptr); + return LinkFieldsHelper::LinkFields(this, self, klass, true, class_size); +} + +// Set the bitmap of reference instance field offsets. +void ClassLinker::CreateReferenceInstanceOffsets(Handle klass) { + uint32_t reference_offsets = 0; + ObjPtr super_class = klass->GetSuperClass(); + // Leave the reference offsets as 0 for mirror::Object (the class field is handled specially). + if (super_class != nullptr) { + reference_offsets = super_class->GetReferenceInstanceOffsets(); + // Compute reference offsets unless our superclass overflowed. + if (reference_offsets != mirror::Class::kClassWalkSuper) { + size_t num_reference_fields = klass->NumReferenceInstanceFieldsDuringLinking(); + if (num_reference_fields != 0u) { + // All of the fields that contain object references are guaranteed be grouped in memory + // starting at an appropriately aligned address after super class object data. + uint32_t start_offset = RoundUp(super_class->GetObjectSize(), + sizeof(mirror::HeapReference)); + uint32_t start_bit = (start_offset - mirror::kObjectHeaderSize) / + sizeof(mirror::HeapReference); + if (start_bit + num_reference_fields > 32) { + reference_offsets = mirror::Class::kClassWalkSuper; + } else { + reference_offsets |= (0xffffffffu << start_bit) & + (0xffffffffu >> (32 - (start_bit + num_reference_fields))); + } + } + } + } + klass->SetReferenceInstanceOffsets(reference_offsets); +} + +ObjPtr ClassLinker::DoResolveString(dex::StringIndex string_idx, + ObjPtr dex_cache) { + StackHandleScope<1> hs(Thread::Current()); + Handle h_dex_cache(hs.NewHandle(dex_cache)); + return DoResolveString(string_idx, h_dex_cache); +} + +ObjPtr ClassLinker::DoResolveString(dex::StringIndex string_idx, + Handle dex_cache) { + const DexFile& dex_file = *dex_cache->GetDexFile(); + uint32_t utf16_length; + const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); + ObjPtr string = intern_table_->InternStrong(utf16_length, utf8_data); + if (string != nullptr) { + dex_cache->SetResolvedString(string_idx, string); + } + return string; +} + +ObjPtr ClassLinker::DoLookupString(dex::StringIndex string_idx, + ObjPtr dex_cache) { + DCHECK(dex_cache != nullptr); + const DexFile& dex_file = *dex_cache->GetDexFile(); + uint32_t utf16_length; + const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); + ObjPtr string = + intern_table_->LookupStrong(Thread::Current(), utf16_length, utf8_data); + if (string != nullptr) { + dex_cache->SetResolvedString(string_idx, string); + } + return string; +} + +ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr referrer) { + return DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); +} + +ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr dex_cache, + ObjPtr class_loader) { + const DexFile& dex_file = *dex_cache->GetDexFile(); + const char* descriptor = dex_file.StringByTypeIdx(type_idx); + ObjPtr type = LookupResolvedType(descriptor, class_loader); + if (type != nullptr) { + DCHECK(type->IsResolved()); + dex_cache->SetResolvedType(type_idx, type); + } + return type; +} + +ObjPtr ClassLinker::LookupResolvedType(const char* descriptor, + ObjPtr class_loader) { + DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; + ObjPtr type = nullptr; + if (descriptor[1] == '\0') { + // only the descriptors of primitive types should be 1 character long, also avoid class lookup + // for primitive classes that aren't backed by dex files. + type = LookupPrimitiveClass(descriptor[0]); + } else { + Thread* const self = Thread::Current(); + DCHECK(self != nullptr); + const size_t hash = ComputeModifiedUtf8Hash(descriptor); + // Find the class in the loaded classes table. + type = LookupClass(self, descriptor, hash, class_loader); + } + return (type != nullptr && type->IsResolved()) ? type : nullptr; +} + +template +ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, RefType referrer) { + StackHandleScope<2> hs(Thread::Current()); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + return DoResolveType(type_idx, dex_cache, class_loader); +} + +// Instantiate the above. +template ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, + ArtField* referrer); +template ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, + ArtMethod* referrer); +template ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, + ObjPtr referrer); + +ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, + Handle dex_cache, + Handle class_loader) { + Thread* self = Thread::Current(); + const char* descriptor = dex_cache->GetDexFile()->StringByTypeIdx(type_idx); + ObjPtr resolved = FindClass(self, descriptor, class_loader); + if (resolved != nullptr) { + // TODO: we used to throw here if resolved's class loader was not the + // boot class loader. This was to permit different classes with the + // same name to be loaded simultaneously by different loaders + dex_cache->SetResolvedType(type_idx, resolved); + } else { + CHECK(self->IsExceptionPending()) + << "Expected pending exception for failed resolution of: " << descriptor; + // Convert a ClassNotFoundException to a NoClassDefFoundError. + StackHandleScope<1> hs(self); + Handle cause(hs.NewHandle(self->GetException())); + if (cause->InstanceOf(GetClassRoot(ClassRoot::kJavaLangClassNotFoundException, this))) { + DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. + self->ClearException(); + ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); + self->GetException()->SetCause(cause.Get()); + } + } + DCHECK((resolved == nullptr) || resolved->IsResolved()) + << resolved->PrettyDescriptor() << " " << resolved->GetStatus(); + return resolved; +} + +ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t method_idx) { + // Search for the method using dex_cache and method_idx. The Class::Find*Method() + // functions can optimize the search if the dex_cache is the same as the DexCache + // of the class, with fall-back to name and signature search otherwise. + ArtMethod* resolved = nullptr; + if (klass->IsInterface()) { + resolved = klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_); + } else { + resolved = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_); + } + DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); + if (resolved != nullptr && + // We pass AccessMethod::kNone instead of kLinking to not warn yet on the + // access, as we'll be looking if the method can be accessed through an + // interface. + hiddenapi::ShouldDenyAccessToMember(resolved, + hiddenapi::AccessContext(class_loader, dex_cache), + hiddenapi::AccessMethod::kNone)) { + // The resolved method that we have found cannot be accessed due to + // hiddenapi (typically it is declared up the hierarchy and is not an SDK + // method). Try to find an interface method from the implemented interfaces which is + // part of the SDK. + ArtMethod* itf_method = klass->FindAccessibleInterfaceMethod(resolved, image_pointer_size_); + if (itf_method == nullptr) { + // No interface method. Call ShouldDenyAccessToMember again but this time + // with AccessMethod::kLinking to ensure that an appropriate warning is + // logged. + hiddenapi::ShouldDenyAccessToMember(resolved, + hiddenapi::AccessContext(class_loader, dex_cache), + hiddenapi::AccessMethod::kLinking); + resolved = nullptr; + } else { + // We found an interface method that is accessible, continue with the resolved method. + } + } + if (resolved != nullptr) { + // In case of jmvti, the dex file gets verified before being registered, so first + // check if it's registered before checking class tables. + const DexFile& dex_file = *dex_cache->GetDexFile(); + DCHECK(!IsDexFileRegistered(Thread::Current(), dex_file) || + FindClassTable(Thread::Current(), dex_cache) == ClassTableForClassLoader(class_loader)) + << "DexFile referrer: " << dex_file.GetLocation() + << " ClassLoader: " << DescribeLoaders(class_loader, ""); + // Be a good citizen and update the dex cache to speed subsequent calls. + dex_cache->SetResolvedMethod(method_idx, resolved); + // Disable the following invariant check as the verifier breaks it. b/73760543 + // const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); + // DCHECK(LookupResolvedType(method_id.class_idx_, dex_cache, class_loader) != nullptr) + // << "Method: " << resolved->PrettyMethod() << ", " + // << "Class: " << klass->PrettyClass() << " (" << klass->GetStatus() << "), " + // << "DexFile referrer: " << dex_file.GetLocation(); + } + return resolved; +} + +// Returns true if `method` is either null or hidden. +// Does not print any warnings if it is hidden. +static bool CheckNoSuchMethod(ArtMethod* method, + ObjPtr dex_cache, + ObjPtr class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) { + return method == nullptr || + hiddenapi::ShouldDenyAccessToMember(method, + hiddenapi::AccessContext(class_loader, dex_cache), + hiddenapi::AccessMethod::kNone); // no warnings +} + +ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t method_idx) { + if (klass->IsInterface()) { + ArtMethod* method = klass->FindClassMethod(dex_cache, method_idx, image_pointer_size_); + return CheckNoSuchMethod(method, dex_cache, class_loader) ? nullptr : method; + } else { + // If there was an interface method with the same signature, we would have + // found it in the "copied" methods. Only DCHECK that the interface method + // really does not exist. + if (kIsDebugBuild) { + ArtMethod* method = + klass->FindInterfaceMethod(dex_cache, method_idx, image_pointer_size_); + DCHECK(CheckNoSuchMethod(method, dex_cache, class_loader)); + } + return nullptr; + } +} + +template +ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, + Handle dex_cache, + Handle class_loader, + ArtMethod* referrer, + InvokeType type) { + DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump(); + DCHECK(dex_cache != nullptr); + DCHECK(referrer == nullptr || !referrer->IsProxyMethod()); + // Check for hit in the dex cache. + ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); + Thread::PoisonObjectPointersIfDebug(); + DCHECK(resolved == nullptr || !resolved->IsRuntimeMethod()); + bool valid_dex_cache_method = resolved != nullptr; + if (kResolveMode == ResolveMode::kNoChecks && valid_dex_cache_method) { + // We have a valid method from the DexCache and no checks to perform. + DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); + return resolved; + } + const DexFile& dex_file = *dex_cache->GetDexFile(); + const dex::MethodId& method_id = dex_file.GetMethodId(method_idx); + ObjPtr klass = nullptr; + if (valid_dex_cache_method) { + // We have a valid method from the DexCache but we need to perform ICCE and IAE checks. + DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); + klass = LookupResolvedType(method_id.class_idx_, dex_cache.Get(), class_loader.Get()); + if (UNLIKELY(klass == nullptr)) { + // We normaly should not end up here. However the verifier currently doesn't guarantee + // the invariant of having the klass in the class table. b/73760543 + klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + // This can only happen if the current thread is not allowed to load + // classes. + DCHECK(!Thread::Current()->CanLoadClasses()); + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + } + } else { + // The method was not in the DexCache, resolve the declaring class. + klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + } + + // Check if the invoke type matches the class type. + if (kResolveMode == ResolveMode::kCheckICCEAndIAE && + CheckInvokeClassMismatch( + dex_cache.Get(), type, [klass]() { return klass; })) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + + if (!valid_dex_cache_method) { + resolved = FindResolvedMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx); + } + + // Note: We can check for IllegalAccessError only if we have a referrer. + if (kResolveMode == ResolveMode::kCheckICCEAndIAE && resolved != nullptr && referrer != nullptr) { + ObjPtr methods_class = resolved->GetDeclaringClass(); + ObjPtr referring_class = referrer->GetDeclaringClass(); + if (!referring_class->CheckResolvedMethodAccess(methods_class, + resolved, + dex_cache.Get(), + method_idx, + type)) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + } + + // If we found a method, check for incompatible class changes. + if (LIKELY(resolved != nullptr) && + LIKELY(kResolveMode == ResolveMode::kNoChecks || + !resolved->CheckIncompatibleClassChange(type))) { + return resolved; + } else { + // If we had a method, or if we can find one with another lookup type, + // it's an incompatible-class-change error. + if (resolved == nullptr) { + resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx); + } + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); + } else { + // We failed to find the method (using all lookup types), so throw a NoSuchMethodError. + const char* name = dex_file.StringDataByIdx(method_id.name_idx_); + const Signature signature = dex_file.GetMethodSignature(method_id); + ThrowNoSuchMethodError(type, klass, name, signature); + } + Thread::Current()->AssertPendingException(); + return nullptr; + } +} + +ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, + Handle dex_cache, + Handle class_loader) { + ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); + Thread::PoisonObjectPointersIfDebug(); + if (resolved != nullptr) { + DCHECK(!resolved->IsRuntimeMethod()); + DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); + return resolved; + } + // Fail, get the declaring class. + const dex::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx); + ObjPtr klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + Thread::Current()->AssertPendingException(); + return nullptr; + } + if (klass->IsInterface()) { + resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); + } else { + resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); + } + if (resolved != nullptr && + hiddenapi::ShouldDenyAccessToMember( + resolved, + hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()), + hiddenapi::AccessMethod::kLinking)) { + resolved = nullptr; + } + return resolved; +} + +ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, + ObjPtr dex_cache, + ObjPtr class_loader, + bool is_static) { + const DexFile& dex_file = *dex_cache->GetDexFile(); + const dex::FieldId& field_id = dex_file.GetFieldId(field_idx); + ObjPtr klass = dex_cache->GetResolvedType(field_id.class_idx_); + if (klass == nullptr) { + klass = LookupResolvedType(field_id.class_idx_, dex_cache, class_loader); + } + if (klass == nullptr) { + // The class has not been resolved yet, so the field is also unresolved. + return nullptr; + } + DCHECK(klass->IsResolved()); + + return FindResolvedField(klass, dex_cache, class_loader, field_idx, is_static); +} + +ArtField* ClassLinker::ResolveField(uint32_t field_idx, + Handle dex_cache, + Handle class_loader, + bool is_static) { + DCHECK(dex_cache != nullptr); + DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump(); + ArtField* resolved = dex_cache->GetResolvedField(field_idx); + Thread::PoisonObjectPointersIfDebug(); + if (resolved != nullptr) { + return resolved; + } + const DexFile& dex_file = *dex_cache->GetDexFile(); + const dex::FieldId& field_id = dex_file.GetFieldId(field_idx); + ObjPtr klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + + resolved = FindResolvedField(klass, dex_cache.Get(), class_loader.Get(), field_idx, is_static); + if (resolved == nullptr) { + const char* name = dex_file.GetFieldName(field_id); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); + ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); + } + return resolved; +} + +ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, + Handle dex_cache, + Handle class_loader) { + DCHECK(dex_cache != nullptr); + ArtField* resolved = dex_cache->GetResolvedField(field_idx); + Thread::PoisonObjectPointersIfDebug(); + if (resolved != nullptr) { + return resolved; + } + const DexFile& dex_file = *dex_cache->GetDexFile(); + const dex::FieldId& field_id = dex_file.GetFieldId(field_idx); + ObjPtr klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + + resolved = FindResolvedFieldJLS(klass, dex_cache.Get(), class_loader.Get(), field_idx); + if (resolved == nullptr) { + const char* name = dex_file.GetFieldName(field_id); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); + ThrowNoSuchFieldError("", klass, type, name); + } + return resolved; +} + +ArtField* ClassLinker::FindResolvedField(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t field_idx, + bool is_static) { + ArtField* resolved = nullptr; + Thread* self = is_static ? Thread::Current() : nullptr; + const DexFile& dex_file = *dex_cache->GetDexFile(); + + resolved = is_static ? mirror::Class::FindStaticField(self, klass, dex_cache, field_idx) + : klass->FindInstanceField(dex_cache, field_idx); + + if (resolved == nullptr) { + const dex::FieldId& field_id = dex_file.GetFieldId(field_idx); + const char* name = dex_file.GetFieldName(field_id); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); + resolved = is_static ? mirror::Class::FindStaticField(self, klass, name, type) + : klass->FindInstanceField(name, type); + } + + if (resolved != nullptr && + hiddenapi::ShouldDenyAccessToMember(resolved, + hiddenapi::AccessContext(class_loader, dex_cache), + hiddenapi::AccessMethod::kLinking)) { + resolved = nullptr; + } + + if (resolved != nullptr) { + dex_cache->SetResolvedField(field_idx, resolved); + } + + return resolved; +} + +ArtField* ClassLinker::FindResolvedFieldJLS(ObjPtr klass, + ObjPtr dex_cache, + ObjPtr class_loader, + uint32_t field_idx) { + ArtField* resolved = nullptr; + Thread* self = Thread::Current(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + const dex::FieldId& field_id = dex_file.GetFieldId(field_idx); + + const char* name = dex_file.GetFieldName(field_id); + const char* type = dex_file.GetFieldTypeDescriptor(field_id); + resolved = mirror::Class::FindField(self, klass, name, type); + + if (resolved != nullptr && + hiddenapi::ShouldDenyAccessToMember(resolved, + hiddenapi::AccessContext(class_loader, dex_cache), + hiddenapi::AccessMethod::kLinking)) { + resolved = nullptr; + } + + if (resolved != nullptr) { + dex_cache->SetResolvedField(field_idx, resolved); + } + + return resolved; +} + +ObjPtr ClassLinker::ResolveMethodType( + Thread* self, + dex::ProtoIndex proto_idx, + Handle dex_cache, + Handle class_loader) { + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + DCHECK(dex_cache != nullptr); + + ObjPtr resolved = dex_cache->GetResolvedMethodType(proto_idx); + if (resolved != nullptr) { + return resolved; + } + + StackHandleScope<4> hs(self); + + // First resolve the return type. + const DexFile& dex_file = *dex_cache->GetDexFile(); + const dex::ProtoId& proto_id = dex_file.GetProtoId(proto_idx); + Handle return_type(hs.NewHandle( + ResolveType(proto_id.return_type_idx_, dex_cache, class_loader))); + if (return_type == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + // Then resolve the argument types. + // + // TODO: Is there a better way to figure out the number of method arguments + // other than by looking at the shorty ? + const size_t num_method_args = strlen(dex_file.StringDataByIdx(proto_id.shorty_idx_)) - 1; + + ObjPtr array_of_class = GetClassRoot>(this); + Handle> method_params(hs.NewHandle( + mirror::ObjectArray::Alloc(self, array_of_class, num_method_args))); + if (method_params == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + DexFileParameterIterator it(dex_file, proto_id); + int32_t i = 0; + MutableHandle param_class = hs.NewHandle(nullptr); + for (; it.HasNext(); it.Next()) { + const dex::TypeIndex type_idx = it.GetTypeIdx(); + param_class.Assign(ResolveType(type_idx, dex_cache, class_loader)); + if (param_class == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + method_params->Set(i++, param_class.Get()); + } + + DCHECK(!it.HasNext()); + + Handle type = hs.NewHandle( + mirror::MethodType::Create(self, return_type, method_params)); + dex_cache->SetResolvedMethodType(proto_idx, type.Get()); + + return type.Get(); +} + +ObjPtr ClassLinker::ResolveMethodType(Thread* self, + dex::ProtoIndex proto_idx, + ArtMethod* referrer) { + StackHandleScope<2> hs(self); + Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + return ResolveMethodType(self, proto_idx, dex_cache, class_loader); +} + +ObjPtr ClassLinker::ResolveMethodHandleForField( + Thread* self, + const dex::MethodHandleItem& method_handle, + ArtMethod* referrer) { + DexFile::MethodHandleType handle_type = + static_cast(method_handle.method_handle_type_); + mirror::MethodHandle::Kind kind; + bool is_put; + bool is_static; + int32_t num_params; + switch (handle_type) { + case DexFile::MethodHandleType::kStaticPut: { + kind = mirror::MethodHandle::Kind::kStaticPut; + is_put = true; + is_static = true; + num_params = 1; + break; + } + case DexFile::MethodHandleType::kStaticGet: { + kind = mirror::MethodHandle::Kind::kStaticGet; + is_put = false; + is_static = true; + num_params = 0; + break; + } + case DexFile::MethodHandleType::kInstancePut: { + kind = mirror::MethodHandle::Kind::kInstancePut; + is_put = true; + is_static = false; + num_params = 2; + break; + } + case DexFile::MethodHandleType::kInstanceGet: { + kind = mirror::MethodHandle::Kind::kInstanceGet; + is_put = false; + is_static = false; + num_params = 1; + break; + } + case DexFile::MethodHandleType::kInvokeStatic: + case DexFile::MethodHandleType::kInvokeInstance: + case DexFile::MethodHandleType::kInvokeConstructor: + case DexFile::MethodHandleType::kInvokeDirect: + case DexFile::MethodHandleType::kInvokeInterface: + UNREACHABLE(); + } + + ArtField* target_field = + ResolveField(method_handle.field_or_method_idx_, referrer, is_static); + if (LIKELY(target_field != nullptr)) { + ObjPtr target_class = target_field->GetDeclaringClass(); + ObjPtr referring_class = referrer->GetDeclaringClass(); + if (UNLIKELY(!referring_class->CanAccessMember(target_class, target_field->GetAccessFlags()))) { + ThrowIllegalAccessErrorField(referring_class, target_field); + return nullptr; + } + if (UNLIKELY(is_put && target_field->IsFinal())) { + ThrowIllegalAccessErrorField(referring_class, target_field); + return nullptr; + } + } else { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + + StackHandleScope<4> hs(self); + ObjPtr array_of_class = GetClassRoot>(this); + Handle> method_params(hs.NewHandle( + mirror::ObjectArray::Alloc(self, array_of_class, num_params))); + if (UNLIKELY(method_params == nullptr)) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + Handle constructor_class; + Handle return_type; + switch (handle_type) { + case DexFile::MethodHandleType::kStaticPut: { + method_params->Set(0, target_field->ResolveType()); + return_type = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, this)); + break; + } + case DexFile::MethodHandleType::kStaticGet: { + return_type = hs.NewHandle(target_field->ResolveType()); + break; + } + case DexFile::MethodHandleType::kInstancePut: { + method_params->Set(0, target_field->GetDeclaringClass()); + method_params->Set(1, target_field->ResolveType()); + return_type = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, this)); + break; + } + case DexFile::MethodHandleType::kInstanceGet: { + method_params->Set(0, target_field->GetDeclaringClass()); + return_type = hs.NewHandle(target_field->ResolveType()); + break; + } + case DexFile::MethodHandleType::kInvokeStatic: + case DexFile::MethodHandleType::kInvokeInstance: + case DexFile::MethodHandleType::kInvokeConstructor: + case DexFile::MethodHandleType::kInvokeDirect: + case DexFile::MethodHandleType::kInvokeInterface: + UNREACHABLE(); + } + + for (int32_t i = 0; i < num_params; ++i) { + if (UNLIKELY(method_params->Get(i) == nullptr)) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + } + + if (UNLIKELY(return_type.IsNull())) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + Handle + method_type(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params))); + if (UNLIKELY(method_type.IsNull())) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + uintptr_t target = reinterpret_cast(target_field); + return mirror::MethodHandleImpl::Create(self, target, kind, method_type); +} + +ObjPtr ClassLinker::ResolveMethodHandleForMethod( + Thread* self, + const dex::MethodHandleItem& method_handle, + ArtMethod* referrer) { + DexFile::MethodHandleType handle_type = + static_cast(method_handle.method_handle_type_); + mirror::MethodHandle::Kind kind; + uint32_t receiver_count = 0; + ArtMethod* target_method = nullptr; + switch (handle_type) { + case DexFile::MethodHandleType::kStaticPut: + case DexFile::MethodHandleType::kStaticGet: + case DexFile::MethodHandleType::kInstancePut: + case DexFile::MethodHandleType::kInstanceGet: + UNREACHABLE(); + case DexFile::MethodHandleType::kInvokeStatic: { + kind = mirror::MethodHandle::Kind::kInvokeStatic; + receiver_count = 0; + target_method = ResolveMethod(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kStatic); + break; + } + case DexFile::MethodHandleType::kInvokeInstance: { + kind = mirror::MethodHandle::Kind::kInvokeVirtual; + receiver_count = 1; + target_method = ResolveMethod(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kVirtual); + break; + } + case DexFile::MethodHandleType::kInvokeConstructor: { + // Constructors are currently implemented as a transform. They + // are special cased later in this method. + kind = mirror::MethodHandle::Kind::kInvokeTransform; + receiver_count = 0; + target_method = ResolveMethod(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kDirect); + break; + } + case DexFile::MethodHandleType::kInvokeDirect: { + kind = mirror::MethodHandle::Kind::kInvokeDirect; + receiver_count = 1; + StackHandleScope<2> hs(self); + // A constant method handle with type kInvokeDirect can refer to + // a method that is private or to a method in a super class. To + // disambiguate the two options, we resolve the method ignoring + // the invocation type to determine if the method is private. We + // then resolve again specifying the intended invocation type to + // force the appropriate checks. + target_method = ResolveMethodWithoutInvokeType(method_handle.field_or_method_idx_, + hs.NewHandle(referrer->GetDexCache()), + hs.NewHandle(referrer->GetClassLoader())); + if (UNLIKELY(target_method == nullptr)) { + break; + } + + if (target_method->IsPrivate()) { + kind = mirror::MethodHandle::Kind::kInvokeDirect; + target_method = ResolveMethod(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kDirect); + } else { + kind = mirror::MethodHandle::Kind::kInvokeSuper; + target_method = ResolveMethod(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kSuper); + if (UNLIKELY(target_method == nullptr)) { + break; + } + // Find the method specified in the parent in referring class + // so invoke-super invokes the method in the parent of the + // referrer. + target_method = + referrer->GetDeclaringClass()->FindVirtualMethodForVirtual(target_method, + kRuntimePointerSize); + } + break; + } + case DexFile::MethodHandleType::kInvokeInterface: { + kind = mirror::MethodHandle::Kind::kInvokeInterface; + receiver_count = 1; + target_method = ResolveMethod(self, + method_handle.field_or_method_idx_, + referrer, + InvokeType::kInterface); + break; + } + } + + if (UNLIKELY(target_method == nullptr)) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + + ObjPtr target_class = target_method->GetDeclaringClass(); + ObjPtr referring_class = referrer->GetDeclaringClass(); + uint32_t access_flags = target_method->GetAccessFlags(); + if (UNLIKELY(!referring_class->CanAccessMember(target_class, access_flags))) { + ThrowIllegalAccessErrorMethod(referring_class, target_method); + return nullptr; + } + + // Calculate the number of parameters from the method shorty. We add the + // receiver count (0 or 1) and deduct one for the return value. + uint32_t shorty_length; + target_method->GetShorty(&shorty_length); + int32_t num_params = static_cast(shorty_length + receiver_count - 1); + + StackHandleScope<5> hs(self); + ObjPtr array_of_class = GetClassRoot>(this); + Handle> method_params(hs.NewHandle( + mirror::ObjectArray::Alloc(self, array_of_class, num_params))); + if (method_params.Get() == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + const DexFile* dex_file = referrer->GetDexFile(); + const dex::MethodId& method_id = dex_file->GetMethodId(method_handle.field_or_method_idx_); + int32_t index = 0; + if (receiver_count != 0) { + // Insert receiver. Use the class identified in the method handle rather than the declaring + // class of the resolved method which may be super class or default interface method + // (b/115964401). + ObjPtr receiver_class = LookupResolvedType(method_id.class_idx_, referrer); + // receiver_class should have been resolved when resolving the target method. + DCHECK(receiver_class != nullptr); + method_params->Set(index++, receiver_class); + } + + const dex::ProtoId& proto_id = dex_file->GetProtoId(method_id.proto_idx_); + DexFileParameterIterator it(*dex_file, proto_id); + while (it.HasNext()) { + DCHECK_LT(index, num_params); + const dex::TypeIndex type_idx = it.GetTypeIdx(); + ObjPtr klass = ResolveType(type_idx, referrer); + if (nullptr == klass) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + method_params->Set(index++, klass); + it.Next(); + } + + Handle return_type = + hs.NewHandle(ResolveType(proto_id.return_type_idx_, referrer)); + if (UNLIKELY(return_type.IsNull())) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + Handle + method_type(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params))); + if (UNLIKELY(method_type.IsNull())) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + if (UNLIKELY(handle_type == DexFile::MethodHandleType::kInvokeConstructor)) { + Handle constructor_class = hs.NewHandle(target_method->GetDeclaringClass()); + Handle lookup = + hs.NewHandle(mirror::MethodHandlesLookup::GetDefault(self)); + return lookup->FindConstructor(self, constructor_class, method_type); + } + + uintptr_t target = reinterpret_cast(target_method); + return mirror::MethodHandleImpl::Create(self, target, kind, method_type); +} + +ObjPtr ClassLinker::ResolveMethodHandle(Thread* self, + uint32_t method_handle_idx, + ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile* const dex_file = referrer->GetDexFile(); + const dex::MethodHandleItem& method_handle = dex_file->GetMethodHandle(method_handle_idx); + switch (static_cast(method_handle.method_handle_type_)) { + case DexFile::MethodHandleType::kStaticPut: + case DexFile::MethodHandleType::kStaticGet: + case DexFile::MethodHandleType::kInstancePut: + case DexFile::MethodHandleType::kInstanceGet: + return ResolveMethodHandleForField(self, method_handle, referrer); + case DexFile::MethodHandleType::kInvokeStatic: + case DexFile::MethodHandleType::kInvokeInstance: + case DexFile::MethodHandleType::kInvokeConstructor: + case DexFile::MethodHandleType::kInvokeDirect: + case DexFile::MethodHandleType::kInvokeInterface: + return ResolveMethodHandleForMethod(self, method_handle, referrer); + } +} + +bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const { + return (entry_point == GetQuickResolutionStub()) || + (quick_resolution_trampoline_ == entry_point); +} + +bool ClassLinker::IsQuickToInterpreterBridge(const void* entry_point) const { + return (entry_point == GetQuickToInterpreterBridge()) || + (quick_to_interpreter_bridge_trampoline_ == entry_point); +} + +bool ClassLinker::IsQuickGenericJniStub(const void* entry_point) const { + return (entry_point == GetQuickGenericJniStub()) || + (quick_generic_jni_trampoline_ == entry_point); +} + +bool ClassLinker::IsJniDlsymLookupStub(const void* entry_point) const { + return entry_point == GetJniDlsymLookupStub() || + (jni_dlsym_lookup_trampoline_ == entry_point); +} + +bool ClassLinker::IsJniDlsymLookupCriticalStub(const void* entry_point) const { + return entry_point == GetJniDlsymLookupCriticalStub() || + (jni_dlsym_lookup_critical_trampoline_ == entry_point); +} + +const void* ClassLinker::GetRuntimeQuickGenericJniStub() const { + return GetQuickGenericJniStub(); +} + +void ClassLinker::SetEntryPointsToInterpreter(ArtMethod* method) const { + if (!method->IsNative()) { + method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } else { + method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); + } +} + +void ClassLinker::SetEntryPointsForObsoleteMethod(ArtMethod* method) const { + DCHECK(method->IsObsolete()); + // We cannot mess with the entrypoints of native methods because they are used to determine how + // large the method's quick stack frame is. Without this information we cannot walk the stacks. + if (!method->IsNative()) { + method->SetEntryPointFromQuickCompiledCode(GetInvokeObsoleteMethodStub()); + } +} + +void ClassLinker::DumpForSigQuit(std::ostream& os) { + ScopedObjectAccess soa(Thread::Current()); + ReaderMutexLock mu(soa.Self(), *Locks::classlinker_classes_lock_); + os << "Zygote loaded classes=" << NumZygoteClasses() << " post zygote classes=" + << NumNonZygoteClasses() << "\n"; + ReaderMutexLock mu2(soa.Self(), *Locks::dex_lock_); + os << "Dumping registered class loaders\n"; + size_t class_loader_index = 0; + for (const ClassLoaderData& class_loader : class_loaders_) { + ObjPtr loader = + ObjPtr::DownCast(soa.Self()->DecodeJObject(class_loader.weak_root)); + if (loader != nullptr) { + os << "#" << class_loader_index++ << " " << loader->GetClass()->PrettyDescriptor() << ": ["; + bool saw_one_dex_file = false; + for (const DexCacheData& dex_cache : dex_caches_) { + if (dex_cache.IsValid() && dex_cache.class_table == class_loader.class_table) { + if (saw_one_dex_file) { + os << ":"; + } + saw_one_dex_file = true; + os << dex_cache.dex_file->GetLocation(); + } + } + os << "]"; + bool found_parent = false; + if (loader->GetParent() != nullptr) { + size_t parent_index = 0; + for (const ClassLoaderData& class_loader2 : class_loaders_) { + ObjPtr loader2 = ObjPtr::DownCast( + soa.Self()->DecodeJObject(class_loader2.weak_root)); + if (loader2 == loader->GetParent()) { + os << ", parent #" << parent_index; + found_parent = true; + break; + } + parent_index++; + } + if (!found_parent) { + os << ", unregistered parent of type " + << loader->GetParent()->GetClass()->PrettyDescriptor(); + } + } else { + os << ", no parent"; + } + os << "\n"; + } + } + os << "Done dumping class loaders\n"; + Runtime* runtime = Runtime::Current(); + os << "Classes initialized: " << runtime->GetStat(KIND_GLOBAL_CLASS_INIT_COUNT) << " in " + << PrettyDuration(runtime->GetStat(KIND_GLOBAL_CLASS_INIT_TIME)) << "\n"; +} + +class CountClassesVisitor : public ClassLoaderVisitor { + public: + CountClassesVisitor() : num_zygote_classes(0), num_non_zygote_classes(0) {} + + void Visit(ObjPtr class_loader) + REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) override { + ClassTable* const class_table = class_loader->GetClassTable(); + if (class_table != nullptr) { + num_zygote_classes += class_table->NumZygoteClasses(class_loader); + num_non_zygote_classes += class_table->NumNonZygoteClasses(class_loader); + } + } + + size_t num_zygote_classes; + size_t num_non_zygote_classes; +}; + +size_t ClassLinker::NumZygoteClasses() const { + CountClassesVisitor visitor; + VisitClassLoaders(&visitor); + return visitor.num_zygote_classes + boot_class_table_->NumZygoteClasses(nullptr); +} + +size_t ClassLinker::NumNonZygoteClasses() const { + CountClassesVisitor visitor; + VisitClassLoaders(&visitor); + return visitor.num_non_zygote_classes + boot_class_table_->NumNonZygoteClasses(nullptr); +} + +size_t ClassLinker::NumLoadedClasses() { + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + // Only return non zygote classes since these are the ones which apps which care about. + return NumNonZygoteClasses(); +} + +pid_t ClassLinker::GetClassesLockOwner() { + return Locks::classlinker_classes_lock_->GetExclusiveOwnerTid(); +} + +pid_t ClassLinker::GetDexLockOwner() { + return Locks::dex_lock_->GetExclusiveOwnerTid(); +} + +void ClassLinker::SetClassRoot(ClassRoot class_root, ObjPtr klass) { + DCHECK(!init_done_); + + DCHECK(klass != nullptr); + DCHECK(klass->GetClassLoader() == nullptr); + + mirror::ObjectArray* class_roots = class_roots_.Read(); + DCHECK(class_roots != nullptr); + DCHECK_LT(static_cast(class_root), static_cast(ClassRoot::kMax)); + int32_t index = static_cast(class_root); + DCHECK(class_roots->Get(index) == nullptr); + class_roots->Set(index, klass); +} + +ObjPtr ClassLinker::CreateWellKnownClassLoader( + Thread* self, + const std::vector& dex_files, + Handle loader_class, + Handle parent_loader, + Handle> shared_libraries) { + + StackHandleScope<5> hs(self); + + ArtField* dex_elements_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements); + + Handle dex_elements_class(hs.NewHandle(dex_elements_field->ResolveType())); + DCHECK(dex_elements_class != nullptr); + DCHECK(dex_elements_class->IsArrayClass()); + Handle> h_dex_elements(hs.NewHandle( + mirror::ObjectArray::Alloc(self, + dex_elements_class.Get(), + dex_files.size()))); + Handle h_dex_element_class = + hs.NewHandle(dex_elements_class->GetComponentType()); + + ArtField* element_file_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + DCHECK_EQ(h_dex_element_class.Get(), element_file_field->GetDeclaringClass()); + + ArtField* cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); + DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->LookupResolvedType()); + + ArtField* file_name_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_fileName); + DCHECK_EQ(file_name_field->GetDeclaringClass(), element_file_field->LookupResolvedType()); + + // Fill the elements array. + int32_t index = 0; + for (const DexFile* dex_file : dex_files) { + StackHandleScope<4> hs2(self); + + // CreateWellKnownClassLoader is only used by gtests and compiler. + // Index 0 of h_long_array is supposed to be the oat file but we can leave it null. + Handle h_long_array = hs2.NewHandle(mirror::LongArray::Alloc( + self, + kDexFileIndexStart + 1)); + DCHECK(h_long_array != nullptr); + h_long_array->Set(kDexFileIndexStart, reinterpret_cast64(dex_file)); + + // Note that this creates a finalizable dalvik.system.DexFile object and a corresponding + // FinalizerReference which will never get cleaned up without a started runtime. + Handle h_dex_file = hs2.NewHandle( + cookie_field->GetDeclaringClass()->AllocObject(self)); + DCHECK(h_dex_file != nullptr); + cookie_field->SetObject(h_dex_file.Get(), h_long_array.Get()); + + Handle h_file_name = hs2.NewHandle( + mirror::String::AllocFromModifiedUtf8(self, dex_file->GetLocation().c_str())); + DCHECK(h_file_name != nullptr); + file_name_field->SetObject(h_dex_file.Get(), h_file_name.Get()); + + Handle h_element = hs2.NewHandle(h_dex_element_class->AllocObject(self)); + DCHECK(h_element != nullptr); + element_file_field->SetObject(h_element.Get(), h_dex_file.Get()); + + h_dex_elements->Set(index, h_element.Get()); + index++; + } + DCHECK_EQ(index, h_dex_elements->GetLength()); + + // Create DexPathList. + Handle h_dex_path_list = hs.NewHandle( + dex_elements_field->GetDeclaringClass()->AllocObject(self)); + DCHECK(h_dex_path_list != nullptr); + // Set elements. + dex_elements_field->SetObject(h_dex_path_list.Get(), h_dex_elements.Get()); + // Create an empty List for the "nativeLibraryDirectories," required for native tests. + // Note: this code is uncommon(oatdump)/testing-only, so don't add further WellKnownClasses + // elements. + { + ArtField* native_lib_dirs = dex_elements_field->GetDeclaringClass()-> + FindDeclaredInstanceField("nativeLibraryDirectories", "Ljava/util/List;"); + DCHECK(native_lib_dirs != nullptr); + ObjPtr list_class = FindSystemClass(self, "Ljava/util/ArrayList;"); + DCHECK(list_class != nullptr); + { + StackHandleScope<1> h_list_scope(self); + Handle h_list_class(h_list_scope.NewHandle(list_class)); + bool list_init = EnsureInitialized(self, h_list_class, true, true); + DCHECK(list_init); + list_class = h_list_class.Get(); + } + ObjPtr list_object = list_class->AllocObject(self); + // Note: we leave the object uninitialized. This must never leak into any non-testing code, but + // is fine for testing. While it violates a Java-code invariant (the elementData field is + // normally never null), as long as one does not try to add elements, this will still + // work. + native_lib_dirs->SetObject(h_dex_path_list.Get(), list_object); + } + + // Create the class loader.. + Handle h_class_loader = hs.NewHandle( + ObjPtr::DownCast(loader_class->AllocObject(self))); + DCHECK(h_class_loader != nullptr); + // Set DexPathList. + ArtField* path_list_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList); + DCHECK(path_list_field != nullptr); + path_list_field->SetObject(h_class_loader.Get(), h_dex_path_list.Get()); + + // Make a pretend boot-classpath. + // TODO: Should we scan the image? + ArtField* const parent_field = + mirror::Class::FindField(self, + h_class_loader->GetClass(), + "parent", + "Ljava/lang/ClassLoader;"); + DCHECK(parent_field != nullptr); + if (parent_loader.Get() == nullptr) { + ScopedObjectAccessUnchecked soa(self); + ObjPtr boot_loader(soa.Decode( + WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self)); + parent_field->SetObject(h_class_loader.Get(), boot_loader); + } else { + parent_field->SetObject(h_class_loader.Get(), parent_loader.Get()); + } + + ArtField* shared_libraries_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + DCHECK(shared_libraries_field != nullptr); + shared_libraries_field->SetObject(h_class_loader.Get(), shared_libraries.Get()); + + return h_class_loader.Get(); +} + +jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, + const std::vector& dex_files, + jclass loader_class, + jobject parent_loader, + jobject shared_libraries) { + CHECK(self->GetJniEnv()->IsSameObject(loader_class, + WellKnownClasses::dalvik_system_PathClassLoader) || + self->GetJniEnv()->IsSameObject(loader_class, + WellKnownClasses::dalvik_system_DelegateLastClassLoader) || + self->GetJniEnv()->IsSameObject(loader_class, + WellKnownClasses::dalvik_system_InMemoryDexClassLoader)); + + // SOAAlreadyRunnable is protected, and we need something to add a global reference. + // We could move the jobject to the callers, but all call-sites do this... + ScopedObjectAccessUnchecked soa(self); + + // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex. + StackHandleScope<4> hs(self); + + Handle h_loader_class = + hs.NewHandle(soa.Decode(loader_class)); + Handle h_parent = + hs.NewHandle(soa.Decode(parent_loader)); + Handle> h_shared_libraries = + hs.NewHandle(soa.Decode>(shared_libraries)); + + ObjPtr loader = CreateWellKnownClassLoader( + self, + dex_files, + h_loader_class, + h_parent, + h_shared_libraries); + + // Make it a global ref and return. + ScopedLocalRef local_ref( + soa.Env(), soa.Env()->AddLocalReference(loader)); + return soa.Env()->NewGlobalRef(local_ref.get()); +} + +jobject ClassLinker::CreatePathClassLoader(Thread* self, + const std::vector& dex_files) { + return CreateWellKnownClassLoader(self, + dex_files, + WellKnownClasses::dalvik_system_PathClassLoader, + nullptr); +} + +void ClassLinker::DropFindArrayClassCache() { + std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot(nullptr)); + find_array_class_cache_next_victim_ = 0; +} + +void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const { + Thread* const self = Thread::Current(); + for (const ClassLoaderData& data : class_loaders_) { + // Need to use DecodeJObject so that we get null for cleared JNI weak globals. + ObjPtr class_loader = ObjPtr::DownCast( + self->DecodeJObject(data.weak_root)); + if (class_loader != nullptr) { + visitor->Visit(class_loader); + } + } +} + +void ClassLinker::VisitAllocators(AllocatorVisitor* visitor) const { + for (const ClassLoaderData& data : class_loaders_) { + LinearAlloc* alloc = data.allocator; + if (alloc != nullptr && !visitor->Visit(alloc)) { + break; + } + } +} + +void ClassLinker::InsertDexFileInToClassLoader(ObjPtr dex_file, + ObjPtr class_loader) { + DCHECK(dex_file != nullptr); + Thread* const self = Thread::Current(); + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* const table = ClassTableForClassLoader(class_loader); + DCHECK(table != nullptr); + if (table->InsertStrongRoot(dex_file) && class_loader != nullptr) { + // It was not already inserted, perform the write barrier to let the GC know the class loader's + // class table was modified. + WriteBarrier::ForEveryFieldWrite(class_loader); + } +} + +void ClassLinker::CleanupClassLoaders() { + Thread* const self = Thread::Current(); + std::vector to_delete; + // Do the delete outside the lock to avoid lock violation in jit code cache. + { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + for (auto it = class_loaders_.begin(); it != class_loaders_.end(); ) { + const ClassLoaderData& data = *it; + // Need to use DecodeJObject so that we get null for cleared JNI weak globals. + ObjPtr class_loader = + ObjPtr::DownCast(self->DecodeJObject(data.weak_root)); + if (class_loader != nullptr) { + ++it; + } else { + VLOG(class_linker) << "Freeing class loader"; + to_delete.push_back(data); + it = class_loaders_.erase(it); + } + } + } + for (ClassLoaderData& data : to_delete) { + // CHA unloading analysis and SingleImplementaion cleanups are required. + DeleteClassLoader(self, data, /*cleanup_cha=*/ true); + } +} + +class ClassLinker::FindVirtualMethodHolderVisitor : public ClassVisitor { + public: + FindVirtualMethodHolderVisitor(const ArtMethod* method, PointerSize pointer_size) + : method_(method), + pointer_size_(pointer_size) {} + + bool operator()(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) override { + if (klass->GetVirtualMethodsSliceUnchecked(pointer_size_).Contains(method_)) { + holder_ = klass; + } + // Return false to stop searching if holder_ is not null. + return holder_ == nullptr; + } + + ObjPtr holder_ = nullptr; + const ArtMethod* const method_; + const PointerSize pointer_size_; +}; + +ObjPtr ClassLinker::GetHoldingClassOfCopiedMethod(ArtMethod* method) { + ScopedTrace trace(__FUNCTION__); // Since this function is slow, have a trace to notify people. + CHECK(method->IsCopied()); + FindVirtualMethodHolderVisitor visitor(method, image_pointer_size_); + VisitClasses(&visitor); + return visitor.holder_; +} + +ObjPtr ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { + return ObjPtr::DownCast(ObjPtr>( + mirror::IfTable::Alloc(self, + GetClassRoot>(this), + ifcount * mirror::IfTable::kMax))); +} + +bool ClassLinker::IsUpdatableBootClassPathDescriptor(const char* descriptor ATTRIBUTE_UNUSED) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +bool ClassLinker::DenyAccessBasedOnPublicSdk(ArtMethod* art_method ATTRIBUTE_UNUSED) const + REQUIRES_SHARED(Locks::mutator_lock_) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +bool ClassLinker::DenyAccessBasedOnPublicSdk(ArtField* art_field ATTRIBUTE_UNUSED) const + REQUIRES_SHARED(Locks::mutator_lock_) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +bool ClassLinker::DenyAccessBasedOnPublicSdk(const char* type_descriptor ATTRIBUTE_UNUSED) const { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +void ClassLinker::SetEnablePublicSdkChecks(bool enabled ATTRIBUTE_UNUSED) { + // Should not be called on ClassLinker, only on AotClassLinker that overrides this. + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); +} + +// Instantiate ClassLinker::ResolveMethod. +template ArtMethod* ClassLinker::ResolveMethod( + uint32_t method_idx, + Handle dex_cache, + Handle class_loader, + ArtMethod* referrer, + InvokeType type); +template ArtMethod* ClassLinker::ResolveMethod( + uint32_t method_idx, + Handle dex_cache, + Handle class_loader, + ArtMethod* referrer, + InvokeType type); + +// Instantiate ClassLinker::AllocClass. +template ObjPtr ClassLinker::AllocClass( + Thread* self, + ObjPtr java_lang_Class, + uint32_t class_size); +template ObjPtr ClassLinker::AllocClass( + Thread* self, + ObjPtr java_lang_Class, + uint32_t class_size); + +} // namespace art diff --git a/code/chapter-06/mikrom_service/Android.bp b/code/chapter-06/mikrom_service/Android.bp new file mode 100644 index 0000000..4aa0d03 --- /dev/null +++ b/code/chapter-06/mikrom_service/Android.bp @@ -0,0 +1,420 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-BSD + // legacy_unencumbered + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "framework-core-sources", + srcs: [ + "**/*.java", + "**/*.aidl", + ], + visibility: ["//frameworks/base"], +} + +filegroup { + name: "IKeyAttestationApplicationIdProvider.aidl", + srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"], +} + +filegroup { + name: "IDropBoxManagerService.aidl", + srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"], +} + +filegroup { + name: "ITracingServiceProxy.aidl", + srcs: ["android/tracing/ITracingServiceProxy.aidl"], +} + +// These are subset of framework-core-sources that are needed by the +// android.test.mock library. The implementation of android.test.mock references +// private members of various components to allow mocking of classes that cannot +// be mocked without access to those internal implementation details. +filegroup { + name: "framework-core-sources-for-test-mock", + srcs: [ + "android/accounts/AccountManagerCallback.java", + "android/accounts/AccountManagerFuture.java", + "android/accounts/AccountManager.java", + "android/accounts/AccountsException.java", + "android/accounts/AuthenticatorException.java", + "android/accounts/OperationCanceledException.java", + "android/app/Application.java", + "android/app/IApplicationThread.aidl", + "android/app/IServiceConnection.aidl", + "android/app/PackageDeleteObserver.java", + "android/content/ComponentCallbacks2.java", + "android/content/ComponentCallbacks.java", + "android/content/ContentInterface.java", + "android/content/ContentProvider.java", + "android/content/ContentProviderNative.java", + "android/content/ContentResolver.java", + "android/content/Context.java", + "android/content/ContextWrapper.java", + "android/content/DialogInterface.java", + "android/content/IContentProvider.java", + "android/content/Intent.java", + "android/content/IntentSender.java", + "android/content/OperationApplicationException.java", + "android/content/pm/ActivityInfo.java", + "android/content/pm/ApplicationInfo.java", + "android/content/pm/InstantAppInfo.java", + "android/content/pm/IPackageDataObserver.aidl", + "android/content/pm/KeySet.java", + "android/content/pm/PackageManager.java", + "android/content/pm/VerifierDeviceIdentity.java", + "android/content/res/Resources.java", + "android/database/CrossProcessCursor.java", + "android/database/CrossProcessCursorWrapper.java", + "android/database/Cursor.java", + "android/database/CursorWrapper.java", + "android/os/Binder.java", + "android/os/Bundle.java", + "android/os/IBinder.java", + "android/os/IInterface.java", + "android/os/Parcelable.java", + "android/os/ParcelFileDescriptor.java", + "android/os/RemoteException.java", + "android/os/storage/VolumeInfo.java", + "android/util/AndroidException.java", + "android/view/DisplayAdjustments.java", + "android/view/ViewDebug.java", + ], + visibility: ["//frameworks/base/test-mock"], +} + +filegroup { + name: "libincident_aidl", + srcs: [ + "android/os/IIncidentDumpCallback.aidl", + "android/os/IIncidentManager.aidl", + "android/os/IIncidentReportStatusListener.aidl", + ], +} + +filegroup { + name: "libvibrator_aidl", + srcs: [ + "android/os/IExternalVibrationController.aidl", + "android/os/IExternalVibratorService.aidl", + ], +} + +filegroup { + name: "libpowermanager_aidl", + srcs: [ + "android/os/Temperature.aidl", + "android/os/CoolingDevice.aidl", + "android/os/IHintManager.aidl", + "android/os/IHintSession.aidl", + "android/os/IThermalEventListener.aidl", + "android/os/IThermalStatusListener.aidl", + "android/os/IThermalService.aidl", + "android/os/IPowerManager.aidl", + "android/os/IMikRomManager.aidl", + ], +} + +genrule { + name: "statslog-framework-java-gen", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --java $(out) --module framework" + + " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource", + out: ["com/android/internal/util/FrameworkStatsLog.java"], +} + +java_library { + name: "uieventloggerlib", + srcs: [ + "com/android/internal/logging/UiEvent.java", + "com/android/internal/logging/UiEventLogger.java", + "com/android/internal/logging/UiEventLoggerImpl.java", + "com/android/internal/logging/InstanceId.java", + "com/android/internal/logging/InstanceIdSequence.java", + ":statslog-framework-java-gen", + ], +} + +filegroup { + name: "framework-services-net-module-wifi-shared-srcs", + srcs: [ + "android/net/DhcpResults.java", + "android/util/LocalLog.java", + ], +} + +// keep these files in sync with the package/Tethering/jarjar-rules.txt and +// package/Connectivity/jarjar-rules.txt for the tethering module and connectivity module. +filegroup { + name: "framework-connectivity-shared-srcs", + srcs: [ + "android/util/IndentingPrintWriter.java", + "android/util/LocalLog.java", + // This should be android.util.IndentingPrintWriter, but it's not available in all branches. + "com/android/internal/util/IndentingPrintWriter.java", + "com/android/internal/util/IState.java", + "com/android/internal/util/MessageUtils.java", + "com/android/internal/util/State.java", + "com/android/internal/util/StateMachine.java", + "com/android/internal/util/WakeupMessage.java", + ], +} + +// keep these files in sync with the apex/jobscheduler/service jarjar-rules.txt for +// the jobscheduler module. +filegroup { + name: "framework-jobscheduler-shared-srcs", + srcs: [ + ":modules-utils-preconditions-srcs", + "com/android/internal/util/ArrayUtils.java", + "com/android/internal/util/BitUtils.java", + "com/android/internal/util/CollectionUtils.java", + "com/android/internal/util/ConcurrentUtils.java", + "com/android/internal/util/DumpUtils.java", + "com/android/internal/util/FastPrintWriter.java", + "com/android/internal/util/FastXmlSerializer.java", + "com/android/internal/util/FunctionalUtils.java", + "com/android/internal/util/ParseUtils.java", + "com/android/internal/util/RingBufferIndices.java", + "com/android/internal/util/StatLogger.java", + "com/android/internal/util/XmlUtils.java", + ], +} + +// Keep these files in sync with the apex/permission/jarjar-rules.txt for the permission module. +filegroup { + name: "framework-permission-s-shared-srcs", + srcs: [ + ":modules-utils-preconditions-srcs", + "com/android/internal/infra/AndroidFuture.java", + "com/android/internal/infra/ServiceConnector.java", + "com/android/internal/infra/AndroidFuture.aidl", + "com/android/internal/infra/IAndroidFuture.aidl", + "android/os/HandlerExecutor.java", + ], +} + +// Keep these files in sync with the apex/permission/jarjar-rules.txt for the permission module. +filegroup { + name: "service-permission-shared-srcs", + srcs: [ + "android/util/IndentingPrintWriter.java", + "com/android/internal/util/dump/DualDumpOutputStream.java", + ], +} + +filegroup { + name: "incremental_aidl", + srcs: [ + "android/os/incremental/IIncrementalServiceConnector.aidl", + "android/os/incremental/IncrementalFileSystemControlParcel.aidl", + ], +} + +filegroup { + name: "dataloader_aidl", + srcs: [ + "android/content/pm/DataLoaderParamsParcel.aidl", + "android/content/pm/DataLoaderType.aidl", + "android/content/pm/FileSystemControlParcel.aidl", + "android/content/pm/IDataLoader.aidl", + "android/content/pm/IDataLoaderManager.aidl", + "android/content/pm/InstallationFileParcel.aidl", + "android/content/pm/InstallationFileLocation.aidl", + "android/content/pm/IDataLoaderStatusListener.aidl", + "android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl", + ], +} + +filegroup { + name: "incremental_manager_aidl", + srcs: [ + "android/os/incremental/IIncrementalService.aidl", + "android/os/incremental/IStorageLoadingProgressListener.aidl", + "android/os/incremental/IncrementalNewFileParams.aidl", + "android/os/incremental/IStorageHealthListener.aidl", + "android/os/incremental/PerUidReadTimeouts.aidl", + "android/os/incremental/StorageHealthCheckParams.aidl", + ], +} + +filegroup { + name: "activity_manager_procstate_aidl", + srcs: [ + "android/app/ProcessStateEnum.aidl", + ], +} + +cc_defaults { + name: "incremental_default", + cflags: [ + "-Wall", + "-Wextra", + "-Wextra-semi", + "-Werror", + "-Wzero-as-null-pointer-constant", + "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION", + ], + shared_libs: [ + "libbinder", + "libutils", + ], + aidl: { + include_dirs: [ + "frameworks/native/aidl/binder", + ], + export_aidl_headers: true, + }, +} + +cc_library { + name: "libincremental_aidl-cpp", + srcs: [ + ":incremental_aidl", + ], + defaults: ["incremental_default"], +} + +cc_library { + name: "libdataloader_aidl-cpp", + srcs: [ + ":dataloader_aidl", + ], + defaults: ["incremental_default"], + shared_libs: [ + "libincremental_aidl-cpp", + ], +} + +cc_library { + name: "libincremental_manager_aidl-cpp", + srcs: [ + ":incremental_manager_aidl", + ], + defaults: ["incremental_default"], + shared_libs: [ + "libincremental_aidl-cpp", + "libdataloader_aidl-cpp", + ], +} + +// Build Rust bindings for PermissionController. Needed by keystore2. +aidl_interface { + name: "android.os.permissions_aidl", + unstable: true, + local_include_dir: ".", + srcs: [ + "android/os/IPermissionController.aidl", + ], + backend: { + rust: { + enabled: true, + }, + }, +} + +// Avoid including Parcelable classes as we don't want to have two copies of +// Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony) +// and TeleService app (packages/services/Telephony). +filegroup { + name: "framework-telephony-common-shared-srcs", + srcs: [ + ":modules-utils-preconditions-srcs", + "android/os/RegistrantList.java", + "android/os/Registrant.java", + "android/util/IndentingPrintWriter.java", + "android/util/LocalLog.java", + "android/util/TimeUtils.java", + "com/android/internal/os/SomeArgs.java", + "com/android/internal/util/AsyncChannel.java", + "com/android/internal/util/AsyncService.java", + "com/android/internal/util/BitwiseInputStream.java", + "com/android/internal/util/FastXmlSerializer.java", + "com/android/internal/util/HexDump.java", + "com/android/internal/util/IState.java", + "com/android/internal/util/IndentingPrintWriter.java", + "com/android/internal/util/State.java", + "com/android/internal/util/StateMachine.java", + "com/android/internal/util/UserIcons.java", + ], +} + +// Avoid including Parcelable classes as we don't want to have two copies of +// Parcelable cross the process. +filegroup { + name: "framework-cellbroadcast-shared-srcs", + srcs: [ + ":modules-utils-preconditions-srcs", + "android/os/HandlerExecutor.java", + "android/util/LocalLog.java", + "com/android/internal/util/IState.java", + "com/android/internal/util/State.java", + "com/android/internal/util/StateMachine.java", + ], +} + +filegroup { + name: "framework-ims-common-shared-srcs", + srcs: [ + ":modules-utils-preconditions-srcs", + "android/os/RegistrantList.java", + "android/os/Registrant.java", + "com/android/internal/os/SomeArgs.java", + ], +} + +// utility classes statically linked into wifi-service +filegroup { + name: "framework-wifi-service-shared-srcs", + srcs: [ + "android/net/InterfaceConfiguration.java", + "android/util/BackupUtils.java", + "android/util/Rational.java", + "com/android/internal/util/FastXmlSerializer.java", + "com/android/internal/util/HexDump.java", + "com/android/internal/util/IState.java", + "com/android/internal/util/MessageUtils.java", + "com/android/internal/util/State.java", + "com/android/internal/util/StateMachine.java", + "com/android/internal/util/WakeupMessage.java", + ], + visibility: [ + "//frameworks/opt/net/wifi/service", + "//packages/modules/Wifi/service", + ], +} + +// protolog start +filegroup { + name: "protolog-common-src", + srcs: [ + "com/android/internal/protolog/common/**/*.java", + ], +} + +java_library { + name: "protolog-lib", + platform_apis: true, + srcs: [ + "com/android/internal/protolog/ProtoLogImpl.java", + "com/android/internal/protolog/ProtoLogViewerConfigReader.java", + ":protolog-common-src", + ], +} + +java_library { + name: "protolog-groups", + srcs: [ + "com/android/internal/protolog/ProtoLogGroup.java", + ":protolog-common-src", + ], +} + +// protolog end diff --git a/code/chapter-06/mikrom_service/Context.java b/code/chapter-06/mikrom_service/Context.java new file mode 100644 index 0000000..5cbd8d3 --- /dev/null +++ b/code/chapter-06/mikrom_service/Context.java @@ -0,0 +1,6922 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.annotation.AttrRes; +import android.annotation.CallbackExecutor; +import android.annotation.CheckResult; +import android.annotation.ColorInt; +import android.annotation.ColorRes; +import android.annotation.DisplayContext; +import android.annotation.DrawableRes; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.StringDef; +import android.annotation.StringRes; +import android.annotation.StyleRes; +import android.annotation.StyleableRes; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.annotation.UiContext; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.app.GameManager; +import android.app.IApplicationThread; +import android.app.IServiceConnection; +import android.app.VrManager; +import android.app.people.PeopleManager; +import android.app.time.TimeManager; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.AssetManager; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.DatabaseErrorHandler; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.IBinder; +import android.os.Looper; +import android.os.StatFs; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.storage.StorageManager; +import android.provider.MediaStore; +import android.telephony.TelephonyRegistryManager; +import android.util.AttributeSet; +import android.view.Display; +import android.view.DisplayAdjustments; +import android.view.View; +import android.view.ViewDebug; +import android.view.ViewGroup.LayoutParams; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams.WindowType; +import android.view.autofill.AutofillManager.AutofillClient; +import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; +import android.view.textclassifier.TextClassificationManager; +import android.window.WindowContext; + +import com.android.internal.compat.IPlatformCompat; +import com.android.internal.compat.IPlatformCompatNative; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Interface to global information about an application environment. This is + * an abstract class whose implementation is provided by + * the Android system. It + * allows access to application-specific resources and classes, as well as + * up-calls for application-level operations such as launching activities, + * broadcasting and receiving intents, etc. + */ +public abstract class Context { + /** @hide */ + @IntDef(flag = true, prefix = { "MODE_" }, value = { + MODE_PRIVATE, + MODE_WORLD_READABLE, + MODE_WORLD_WRITEABLE, + MODE_APPEND, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FileMode {} + + /** @hide */ + @IntDef(flag = true, prefix = { "MODE_" }, value = { + MODE_PRIVATE, + MODE_WORLD_READABLE, + MODE_WORLD_WRITEABLE, + MODE_MULTI_PROCESS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PreferencesMode {} + + /** @hide */ + @IntDef(flag = true, prefix = { "MODE_" }, value = { + MODE_PRIVATE, + MODE_WORLD_READABLE, + MODE_WORLD_WRITEABLE, + MODE_ENABLE_WRITE_AHEAD_LOGGING, + MODE_NO_LOCALIZED_COLLATORS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DatabaseMode {} + + /** + * File creation mode: the default mode, where the created file can only + * be accessed by the calling application (or all applications sharing the + * same user ID). + */ + public static final int MODE_PRIVATE = 0x0000; + + /** + * File creation mode: allow all other applications to have read access to + * the created file. + *

+ * Starting from {@link android.os.Build.VERSION_CODES#N}, attempting to use this + * mode throws a {@link SecurityException}. + * + * @deprecated Creating world-readable files is very dangerous, and likely + * to cause security holes in applications. It is strongly + * discouraged; instead, applications should use more formal + * mechanism for interactions such as {@link ContentProvider}, + * {@link BroadcastReceiver}, and {@link android.app.Service}. + * There are no guarantees that this access mode will remain on + * a file, such as when it goes through a backup and restore. + * @see android.support.v4.content.FileProvider + * @see Intent#FLAG_GRANT_WRITE_URI_PERMISSION + */ + @Deprecated + public static final int MODE_WORLD_READABLE = 0x0001; + + /** + * File creation mode: allow all other applications to have write access to + * the created file. + *

+ * Starting from {@link android.os.Build.VERSION_CODES#N}, attempting to use this + * mode will throw a {@link SecurityException}. + * + * @deprecated Creating world-writable files is very dangerous, and likely + * to cause security holes in applications. It is strongly + * discouraged; instead, applications should use more formal + * mechanism for interactions such as {@link ContentProvider}, + * {@link BroadcastReceiver}, and {@link android.app.Service}. + * There are no guarantees that this access mode will remain on + * a file, such as when it goes through a backup and restore. + * @see android.support.v4.content.FileProvider + * @see Intent#FLAG_GRANT_WRITE_URI_PERMISSION + */ + @Deprecated + public static final int MODE_WORLD_WRITEABLE = 0x0002; + + /** + * File creation mode: for use with {@link #openFileOutput}, if the file + * already exists then write data to the end of the existing file + * instead of erasing it. + * @see #openFileOutput + */ + public static final int MODE_APPEND = 0x8000; + + /** + * SharedPreference loading flag: when set, the file on disk will + * be checked for modification even if the shared preferences + * instance is already loaded in this process. This behavior is + * sometimes desired in cases where the application has multiple + * processes, all writing to the same SharedPreferences file. + * Generally there are better forms of communication between + * processes, though. + * + *

This was the legacy (but undocumented) behavior in and + * before Gingerbread (Android 2.3) and this flag is implied when + * targeting such releases. For applications targeting SDK + * versions greater than Android 2.3, this flag must be + * explicitly set if desired. + * + * @see #getSharedPreferences + * + * @deprecated MODE_MULTI_PROCESS does not work reliably in + * some versions of Android, and furthermore does not provide any + * mechanism for reconciling concurrent modifications across + * processes. Applications should not attempt to use it. Instead, + * they should use an explicit cross-process data management + * approach such as {@link android.content.ContentProvider ContentProvider}. + */ + @Deprecated + public static final int MODE_MULTI_PROCESS = 0x0004; + + /** + * Database open flag: when set, the database is opened with write-ahead + * logging enabled by default. + * + * @see #openOrCreateDatabase(String, int, CursorFactory) + * @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler) + * @see SQLiteDatabase#enableWriteAheadLogging + */ + public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0x0008; + + /** + * Database open flag: when set, the database is opened without support for + * localized collators. + * + * @see #openOrCreateDatabase(String, int, CursorFactory) + * @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler) + * @see SQLiteDatabase#NO_LOCALIZED_COLLATORS + */ + public static final int MODE_NO_LOCALIZED_COLLATORS = 0x0010; + + /** @hide */ + @IntDef(flag = true, prefix = { "BIND_" }, value = { + BIND_AUTO_CREATE, + BIND_DEBUG_UNBIND, + BIND_NOT_FOREGROUND, + BIND_ABOVE_CLIENT, + BIND_ALLOW_OOM_MANAGEMENT, + BIND_WAIVE_PRIORITY, + BIND_IMPORTANT, + BIND_ADJUST_WITH_ACTIVITY, + BIND_NOT_PERCEPTIBLE, + BIND_INCLUDE_CAPABILITIES + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BindServiceFlags {} + + /** + * Flag for {@link #bindService}: automatically create the service as long + * as the binding exists. Note that while this will create the service, + * its {@link android.app.Service#onStartCommand} + * method will still only be called due to an + * explicit call to {@link #startService}. Even without that, though, + * this still provides you with access to the service object while the + * service is created. + * + *

Note that prior to {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, + * not supplying this flag would also impact how important the system + * consider's the target service's process to be. When set, the only way + * for it to be raised was by binding from a service in which case it will + * only be important when that activity is in the foreground. Now to + * achieve this behavior you must explicitly supply the new flag + * {@link #BIND_ADJUST_WITH_ACTIVITY}. For compatibility, old applications + * that don't specify {@link #BIND_AUTO_CREATE} will automatically have + * the flags {@link #BIND_WAIVE_PRIORITY} and + * {@link #BIND_ADJUST_WITH_ACTIVITY} set for them in order to achieve + * the same result. + */ + public static final int BIND_AUTO_CREATE = 0x0001; + + /** + * Flag for {@link #bindService}: include debugging help for mismatched + * calls to unbind. When this flag is set, the callstack of the following + * {@link #unbindService} call is retained, to be printed if a later + * incorrect unbind call is made. Note that doing this requires retaining + * information about the binding that was made for the lifetime of the app, + * resulting in a leak -- this should only be used for debugging. + */ + public static final int BIND_DEBUG_UNBIND = 0x0002; + + /** + * Flag for {@link #bindService}: don't allow this binding to raise + * the target service's process to the foreground scheduling priority. + * It will still be raised to at least the same memory priority + * as the client (so that its process will not be killable in any + * situation where the client is not killable), but for CPU scheduling + * purposes it may be left in the background. This only has an impact + * in the situation where the binding client is a foreground process + * and the target service is in a background process. + */ + public static final int BIND_NOT_FOREGROUND = 0x0004; + + /** + * Flag for {@link #bindService}: indicates that the client application + * binding to this service considers the service to be more important than + * the app itself. When set, the platform will try to have the out of + * memory killer kill the app before it kills the service it is bound to, though + * this is not guaranteed to be the case. + */ + public static final int BIND_ABOVE_CLIENT = 0x0008; + + /** + * Flag for {@link #bindService}: allow the process hosting the bound + * service to go through its normal memory management. It will be + * treated more like a running service, allowing the system to + * (temporarily) expunge the process if low on memory or for some other + * whim it may have, and being more aggressive about making it a candidate + * to be killed (and restarted) if running for a long time. + */ + public static final int BIND_ALLOW_OOM_MANAGEMENT = 0x0010; + + /** + * Flag for {@link #bindService}: don't impact the scheduling or + * memory management priority of the target service's hosting process. + * Allows the service's process to be managed on the background LRU list + * just like a regular application process in the background. + */ + public static final int BIND_WAIVE_PRIORITY = 0x0020; + + /** + * Flag for {@link #bindService}: this service is very important to + * the client, so should be brought to the foreground process level + * when the client is. Normally a process can only be raised to the + * visibility level by a client, even if that client is in the foreground. + */ + public static final int BIND_IMPORTANT = 0x0040; + + /** + * Flag for {@link #bindService}: If binding from an activity, allow the + * target service's process importance to be raised based on whether the + * activity is visible to the user, regardless whether another flag is + * used to reduce the amount that the client process's overall importance + * is used to impact it. + */ + public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080; + + /** + * Flag for {@link #bindService}: If binding from an app that is visible or user-perceptible, + * lower the target service's importance to below the perceptible level. This allows + * the system to (temporarily) expunge the bound process from memory to make room for more + * important user-perceptible processes. + */ + public static final int BIND_NOT_PERCEPTIBLE = 0x00000100; + + /** + * Flag for {@link #bindService}: If binding from an app that has specific capabilities + * due to its foreground state such as an activity or foreground service, then this flag will + * allow the bound app to get the same capabilities, as long as it has the required permissions + * as well. + * + * If binding from a top app and its target SDK version is at or above + * {@link android.os.Build.VERSION_CODES#R}, the app needs to + * explicitly use BIND_INCLUDE_CAPABILITIES flag to pass all capabilities to the service so the + * other app can have while-use-use access such as location, camera, microphone from background. + * If binding from a top app and its target SDK version is below + * {@link android.os.Build.VERSION_CODES#R}, BIND_INCLUDE_CAPABILITIES is implicit. + */ + public static final int BIND_INCLUDE_CAPABILITIES = 0x000001000; + + /*********** Public flags above this line ***********/ + /*********** Hidden flags below this line ***********/ + + /** + * Flag for {@link #bindService}: This flag is only intended to be used by the system to + * indicate that a service binding is not considered as real package component usage and should + * not generate a {@link android.app.usage.UsageEvents.Event#APP_COMPONENT_USED} event in usage + * stats. + * @hide + */ + public static final int BIND_NOT_APP_COMPONENT_USAGE = 0x00008000; + + /** + * Flag for {@link #bindService}: allow the process hosting the target service to be treated + * as if it's as important as a perceptible app to the user and avoid the oom killer killing + * this process in low memory situations until there aren't any other processes left but the + * ones which are user-perceptible. + * + * @hide + */ + public static final int BIND_ALMOST_PERCEPTIBLE = 0x000010000; + + /** + * Flag for {@link #bindService}: allow the process hosting the target service to gain + * {@link ActivityManager#PROCESS_CAPABILITY_NETWORK}, which allows it be able + * to access network regardless of any power saving restrictions. + * + * @hide + */ + public static final int BIND_BYPASS_POWER_NETWORK_RESTRICTIONS = 0x00020000; + + /** + * Do not use. This flag is no longer needed nor used. + * @hide + */ + @SystemApi + public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 0x00040000; + + /** + * Flag for {@link #bindService}: This flag is intended to be used only by the system to adjust + * the scheduling policy for IMEs (and any other out-of-process user-visible components that + * work closely with the top app) so that UI hosted in such services can have the same + * scheduling policy (e.g. SCHED_FIFO when it is enabled and TOP_APP_PRIORITY_BOOST otherwise) + * as the actual top-app. + * @hide + */ + public static final int BIND_SCHEDULE_LIKE_TOP_APP = 0x00080000; + + /** + * Flag for {@link #bindService}: allow background activity starts from the bound service's + * process. + * This flag is only respected if the caller is holding + * {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}. + * @hide + */ + @SystemApi + public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000; + + /** + * @hide Flag for {@link #bindService}: the service being bound to represents a + * protected system component, so must have association restrictions applied to it. + * That is, a system config must have one or more allow-association tags limiting + * which packages it can interact with. If it does not have any such association + * restrictions, a default empty set will be created. + */ + public static final int BIND_RESTRICT_ASSOCIATIONS = 0x00200000; + + /** + * @hide Flag for {@link #bindService}: allows binding to a service provided + * by an instant app. Note that the caller may not have access to the instant + * app providing the service which is a violation of the instant app sandbox. + * This flag is intended ONLY for development/testing and should be used with + * great care. Only the system is allowed to use this flag. + */ + public static final int BIND_ALLOW_INSTANT = 0x00400000; + + /** + * @hide Flag for {@link #bindService}: like {@link #BIND_NOT_FOREGROUND}, but puts it + * up in to the important background state (instead of transient). + */ + public static final int BIND_IMPORTANT_BACKGROUND = 0x00800000; + + /** + * @hide Flag for {@link #bindService}: allows application hosting service to manage whitelists + * such as temporary allowing a {@code PendingIntent} to bypass Power Save mode. + */ + public static final int BIND_ALLOW_WHITELIST_MANAGEMENT = 0x01000000; + + /** + * @hide Flag for {@link #bindService}: Like {@link #BIND_FOREGROUND_SERVICE}, + * but only applies while the device is awake. + */ + public static final int BIND_FOREGROUND_SERVICE_WHILE_AWAKE = 0x02000000; + + /** + * @hide Flag for {@link #bindService}: For only the case where the binding + * is coming from the system, set the process state to FOREGROUND_SERVICE + * instead of the normal maximum of IMPORTANT_FOREGROUND. That is, this is + * saying that the process shouldn't participate in the normal power reduction + * modes (removing network access etc). + */ + public static final int BIND_FOREGROUND_SERVICE = 0x04000000; + + /** + * @hide Flag for {@link #bindService}: Treat the binding as hosting + * an activity, an unbinding as the activity going in the background. + * That is, when unbinding, the process when empty will go on the activity + * LRU list instead of the regular one, keeping it around more aggressively + * than it otherwise would be. This is intended for use with IMEs to try + * to keep IME processes around for faster keyboard switching. + */ + public static final int BIND_TREAT_LIKE_ACTIVITY = 0x08000000; + + /** + * @hide An idea that is not yet implemented. + * Flag for {@link #bindService}: If binding from an activity, consider + * this service to be visible like the binding activity is. That is, + * it will be treated as something more important to keep around than + * invisible background activities. This will impact the number of + * recent activities the user can switch between without having them + * restart. There is no guarantee this will be respected, as the system + * tries to balance such requests from one app vs. the importance of + * keeping other apps around. + */ + public static final int BIND_VISIBLE = 0x10000000; + + /** + * @hide + * Flag for {@link #bindService}: Consider this binding to be causing the target + * process to be showing UI, so it will be do a UI_HIDDEN memory trim when it goes + * away. + */ + public static final int BIND_SHOWING_UI = 0x20000000; + + /** + * Flag for {@link #bindService}: Don't consider the bound service to be + * visible, even if the caller is visible. + * @hide + */ + public static final int BIND_NOT_VISIBLE = 0x40000000; + + /** + * Flag for {@link #bindService}: The service being bound is an + * {@link android.R.attr#isolatedProcess isolated}, + * {@link android.R.attr#externalService external} service. This binds the service into the + * calling application's package, rather than the package in which the service is declared. + *

+ * When using this flag, the code for the service being bound will execute under the calling + * application's package name and user ID. Because the service must be an isolated process, + * it will not have direct access to the application's data, though. + * + * The purpose of this flag is to allow applications to provide services that are attributed + * to the app using the service, rather than the application providing the service. + *

+ */ + public static final int BIND_EXTERNAL_SERVICE = 0x80000000; + + /** + * These bind flags reduce the strength of the binding such that we shouldn't + * consider it as pulling the process up to the level of the one that is bound to it. + * @hide + */ + public static final int BIND_REDUCTION_FLAGS = + Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_WAIVE_PRIORITY + | Context.BIND_NOT_PERCEPTIBLE | Context.BIND_NOT_VISIBLE; + + /** @hide */ + @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = { + RECEIVER_VISIBLE_TO_INSTANT_APPS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RegisterReceiverFlags {} + + /** + * Flag for {@link #registerReceiver}: The receiver can receive broadcasts from Instant Apps. + */ + public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 0x1; + + /** + * Returns an AssetManager instance for the application's package. + *

+ * Note: Implementations of this method should return + * an AssetManager instance that is consistent with the Resources instance + * returned by {@link #getResources()}. For example, they should share the + * same {@link Configuration} object. + * + * @return an AssetManager instance for the application's package + * @see #getResources() + */ + public abstract AssetManager getAssets(); + + /** + * Returns a Resources instance for the application's package. + *

+ * Note: Implementations of this method should return + * a Resources instance that is consistent with the AssetManager instance + * returned by {@link #getAssets()}. For example, they should share the + * same {@link Configuration} object. + * + * @return a Resources instance for the application's package + * @see #getAssets() + */ + public abstract Resources getResources(); + + /** Return PackageManager instance to find global package information. */ + public abstract PackageManager getPackageManager(); + + /** Return a ContentResolver instance for your application's package. */ + public abstract ContentResolver getContentResolver(); + + /** + * Return the Looper for the main thread of the current process. This is + * the thread used to dispatch calls to application components (activities, + * services, etc). + *

+ * By definition, this method returns the same result as would be obtained + * by calling {@link Looper#getMainLooper() Looper.getMainLooper()}. + *

+ * + * @return The main looper. + */ + public abstract Looper getMainLooper(); + + /** + * Return an {@link Executor} that will run enqueued tasks on the main + * thread associated with this context. This is the thread used to dispatch + * calls to application components (activities, services, etc). + */ + public Executor getMainExecutor() { + // This is pretty inefficient, which is why ContextImpl overrides it + return new HandlerExecutor(new Handler(getMainLooper())); + } + + /** + * Return the context of the single, global Application object of the + * current process. This generally should only be used if you need a + * Context whose lifecycle is separate from the current context, that is + * tied to the lifetime of the process rather than the current component. + * + *

Consider for example how this interacts with + * {@link #registerReceiver(BroadcastReceiver, IntentFilter)}: + *

    + *
  • If used from an Activity context, the receiver is being registered + * within that activity. This means that you are expected to unregister + * before the activity is done being destroyed; in fact if you do not do + * so, the framework will clean up your leaked registration as it removes + * the activity and log an error. Thus, if you use the Activity context + * to register a receiver that is static (global to the process, not + * associated with an Activity instance) then that registration will be + * removed on you at whatever point the activity you used is destroyed. + *

  • If used from the Context returned here, the receiver is being + * registered with the global state associated with your application. Thus + * it will never be unregistered for you. This is necessary if the receiver + * is associated with static data, not a particular component. However + * using the ApplicationContext elsewhere can easily lead to serious leaks + * if you forget to unregister, unbind, etc. + *

+ */ + public abstract Context getApplicationContext(); + + /** Non-activity related autofill ids are unique in the app */ + private static int sLastAutofillId = View.NO_ID; + + /** + * Gets the next autofill ID. + * + *

All IDs will be smaller or the same as {@link View#LAST_APP_AUTOFILL_ID}. All IDs + * returned will be unique. + * + * @return A ID that is unique in the process + * + * {@hide} + */ + public int getNextAutofillId() { + if (sLastAutofillId == View.LAST_APP_AUTOFILL_ID - 1) { + sLastAutofillId = View.NO_ID; + } + + sLastAutofillId++; + + return sLastAutofillId; + } + + /** + * Add a new {@link ComponentCallbacks} to the base application of the + * Context, which will be called at the same times as the ComponentCallbacks + * methods of activities and other components are called. Note that you + * must be sure to use {@link #unregisterComponentCallbacks} when + * appropriate in the future; this will not be removed for you. + *

+ * After {@link Build.VERSION_CODES#S}, Registering the ComponentCallbacks to Context created + * via {@link #createWindowContext(int, Bundle)} or + * {@link #createWindowContext(Display, int, Bundle)} will receive + * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} from Window Context rather + * than its base application. It is helpful if you want to handle UI components that + * associated with the Window Context when the Window Context has configuration changes.

+ * + * @param callback The interface to call. This can be either a + * {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface. + * + * @see Context#createWindowContext(int, Bundle) + */ + public void registerComponentCallbacks(ComponentCallbacks callback) { + getApplicationContext().registerComponentCallbacks(callback); + } + + /** + * Remove a {@link ComponentCallbacks} object that was previously registered + * with {@link #registerComponentCallbacks(ComponentCallbacks)}. + */ + public void unregisterComponentCallbacks(ComponentCallbacks callback) { + getApplicationContext().unregisterComponentCallbacks(callback); + } + + /** + * Return a localized, styled CharSequence from the application's package's + * default string table. + * + * @param resId Resource id for the CharSequence text + */ + @NonNull + public final CharSequence getText(@StringRes int resId) { + return getResources().getText(resId); + } + + /** + * Returns a localized string from the application's package's + * default string table. + * + * @param resId Resource id for the string + * @return The string data associated with the resource, stripped of styled + * text information. + */ + @NonNull + public final String getString(@StringRes int resId) { + return getResources().getString(resId); + } + + /** + * Returns a localized formatted string from the application's package's + * default string table, substituting the format arguments as defined in + * {@link java.util.Formatter} and {@link java.lang.String#format}. + * + * @param resId Resource id for the format string + * @param formatArgs The format arguments that will be used for + * substitution. + * @return The string data associated with the resource, formatted and + * stripped of styled text information. + */ + @NonNull + public final String getString(@StringRes int resId, Object... formatArgs) { + return getResources().getString(resId, formatArgs); + } + + /** + * Returns a color associated with a particular resource ID and styled for + * the current theme. + * + * @param id The desired resource identifier, as generated by the aapt + * tool. This integer encodes the package, type, and resource + * entry. The value 0 is an invalid identifier. + * @return A single color value in the form 0xAARRGGBB. + * @throws android.content.res.Resources.NotFoundException if the given ID + * does not exist. + */ + @ColorInt + public final int getColor(@ColorRes int id) { + return getResources().getColor(id, getTheme()); + } + + /** + * Returns a drawable object associated with a particular resource ID and + * styled for the current theme. + * + * @param id The desired resource identifier, as generated by the aapt + * tool. This integer encodes the package, type, and resource + * entry. The value 0 is an invalid identifier. + * @return An object that can be used to draw this resource. + * @throws android.content.res.Resources.NotFoundException if the given ID + * does not exist. + */ + @Nullable + public final Drawable getDrawable(@DrawableRes int id) { + return getResources().getDrawable(id, getTheme()); + } + + /** + * Returns a color state list associated with a particular resource ID and + * styled for the current theme. + * + * @param id The desired resource identifier, as generated by the aapt + * tool. This integer encodes the package, type, and resource + * entry. The value 0 is an invalid identifier. + * @return A color state list. + * @throws android.content.res.Resources.NotFoundException if the given ID + * does not exist. + */ + @NonNull + public final ColorStateList getColorStateList(@ColorRes int id) { + return getResources().getColorStateList(id, getTheme()); + } + + /** + * Set the base theme for this context. Note that this should be called + * before any views are instantiated in the Context (for example before + * calling {@link android.app.Activity#setContentView} or + * {@link android.view.LayoutInflater#inflate}). + * + * @param resid The style resource describing the theme. + */ + public abstract void setTheme(@StyleRes int resid); + + /** @hide Needed for some internal implementation... not public because + * you can't assume this actually means anything. */ + @UnsupportedAppUsage + public int getThemeResId() { + return 0; + } + + /** + * Return the Theme object associated with this Context. + */ + @ViewDebug.ExportedProperty(deepExport = true) + public abstract Resources.Theme getTheme(); + + /** + * Retrieve styled attribute information in this Context's theme. See + * {@link android.content.res.Resources.Theme#obtainStyledAttributes(int[])} + * for more information. + * + * @see android.content.res.Resources.Theme#obtainStyledAttributes(int[]) + */ + @NonNull + public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) { + return getTheme().obtainStyledAttributes(attrs); + } + + /** + * Retrieve styled attribute information in this Context's theme. See + * {@link android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])} + * for more information. + * + * @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[]) + */ + @NonNull + public final TypedArray obtainStyledAttributes(@StyleRes int resid, + @NonNull @StyleableRes int[] attrs) throws Resources.NotFoundException { + return getTheme().obtainStyledAttributes(resid, attrs); + } + + /** + * Retrieve styled attribute information in this Context's theme. See + * {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)} + * for more information. + * + * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int) + */ + @NonNull + public final TypedArray obtainStyledAttributes( + @Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs) { + return getTheme().obtainStyledAttributes(set, attrs, 0, 0); + } + + /** + * Retrieve styled attribute information in this Context's theme. See + * {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)} + * for more information. + * + * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int) + */ + @NonNull + public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set, + @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes) { + return getTheme().obtainStyledAttributes( + set, attrs, defStyleAttr, defStyleRes); + } + + /** + * Return a class loader you can use to retrieve classes in this package. + */ + public abstract ClassLoader getClassLoader(); + + /** Return the name of this application's package. */ + public abstract String getPackageName(); + + /** + * @hide Return the name of the base context this context is derived from. + * This is the same as {@link #getOpPackageName()} except in + * cases where system components are loaded into other app processes, in which + * case {@link #getOpPackageName()} will be the name of the primary package in + * that process (so that app ops uid verification will work with the name). + */ + @SuppressWarnings("HiddenAbstractMethod") + @UnsupportedAppUsage + public abstract String getBasePackageName(); + + /** + * Return the package name that should be used for {@link android.app.AppOpsManager} calls from + * this context, so that app ops manager's uid verification will work with the name. + *

+ * This is not generally intended for third party application developers. + */ + @NonNull + public String getOpPackageName() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + *

Attribution can be used in complex apps to logically separate parts of the app. E.g. a + * blogging app might also have a instant messaging app built in. In this case two separate tags + * can for used each sub-feature. + * + * @return the attribution tag this context is for or {@code null} if this is the default. + */ + public @Nullable String getAttributionTag() { + return null; + } + + /** + * @return The identity of this context for permission purposes. + * + * @see AttributionSource + */ + public @NonNull AttributionSource getAttributionSource() { + return null; + } + + // TODO moltmann: Remove + /** + * @removed + */ + @Deprecated + public @Nullable String getFeatureId() { + return getAttributionTag(); + } + + /** + * Return the set of parameters which this Context was created with, if it + * was created via {@link #createContext(ContextParams)}. + */ + public @Nullable ContextParams getParams() { + return null; + } + + /** Return the full application info for this context's package. */ + public abstract ApplicationInfo getApplicationInfo(); + + /** + * Return the full path to this context's primary Android package. + * The Android package is a ZIP file which contains the application's + * primary resources. + * + *

Note: this is not generally useful for applications, since they should + * not be directly accessing the file system. + * + * @return String Path to the resources. + */ + public abstract String getPackageResourcePath(); + + /** + * Return the full path to this context's primary Android package. + * The Android package is a ZIP file which contains application's + * primary code and assets. + * + *

Note: this is not generally useful for applications, since they should + * not be directly accessing the file system. + * + * @return String Path to the code and assets. + */ + public abstract String getPackageCodePath(); + + /** + * @hide + * @deprecated use {@link #getSharedPreferencesPath(String)} + */ + @Deprecated + @UnsupportedAppUsage + public File getSharedPrefsFile(String name) { + return getSharedPreferencesPath(name); + } + + /** + * Retrieve and hold the contents of the preferences file 'name', returning + * a SharedPreferences through which you can retrieve and modify its + * values. Only one instance of the SharedPreferences object is returned + * to any callers for the same name, meaning they will see each other's + * edits as soon as they are made. + * + *

This method is thread-safe. + * + *

If the preferences directory does not already exist, it will be created when this method + * is called. + * + *

If a preferences file by this name does not exist, it will be created when you retrieve an + * editor ({@link SharedPreferences#edit()}) and then commit changes ({@link + * SharedPreferences.Editor#commit()} or {@link SharedPreferences.Editor#apply()}). + * + * @param name Desired preferences file. + * @param mode Operating mode. + * + * @return The single {@link SharedPreferences} instance that can be used + * to retrieve and modify the preference values. + * + * @see #MODE_PRIVATE + */ + public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode); + + /** + * Retrieve and hold the contents of the preferences file, returning + * a SharedPreferences through which you can retrieve and modify its + * values. Only one instance of the SharedPreferences object is returned + * to any callers for the same name, meaning they will see each other's + * edits as soon as they are made. + * + * @param file Desired preferences file. If a preferences file by this name + * does not exist, it will be created when you retrieve an + * editor (SharedPreferences.edit()) and then commit changes (Editor.commit()). + * @param mode Operating mode. + * + * @return The single {@link SharedPreferences} instance that can be used + * to retrieve and modify the preference values. + * + * @see #getSharedPreferencesPath(String) + * @see #MODE_PRIVATE + * @removed + */ + @SuppressWarnings("HiddenAbstractMethod") + public abstract SharedPreferences getSharedPreferences(File file, @PreferencesMode int mode); + + /** + * Move an existing shared preferences file from the given source storage + * context to this context. This is typically used to migrate data between + * storage locations after an upgrade, such as moving to device protected + * storage. + * + * @param sourceContext The source context which contains the existing + * shared preferences to move. + * @param name The name of the shared preferences file. + * @return {@code true} if the move was successful or if the shared + * preferences didn't exist in the source context, otherwise + * {@code false}. + * @see #createDeviceProtectedStorageContext() + */ + public abstract boolean moveSharedPreferencesFrom(Context sourceContext, String name); + + /** + * Delete an existing shared preferences file. + * + * @param name The name (unique in the application package) of the shared + * preferences file. + * @return {@code true} if the shared preferences file was successfully + * deleted; else {@code false}. + * @see #getSharedPreferences(String, int) + */ + public abstract boolean deleteSharedPreferences(String name); + + /** @hide */ + @SuppressWarnings("HiddenAbstractMethod") + public abstract void reloadSharedPreferences(); + + /** + * Open a private file associated with this Context's application package + * for reading. + * + * @param name The name of the file to open; can not contain path + * separators. + * + * @return The resulting {@link FileInputStream}. + * + * @see #openFileOutput + * @see #fileList + * @see #deleteFile + * @see java.io.FileInputStream#FileInputStream(String) + */ + public abstract FileInputStream openFileInput(String name) + throws FileNotFoundException; + + /** + * Open a private file associated with this Context's application package + * for writing. Creates the file if it doesn't already exist. + *

+ * No additional permissions are required for the calling app to read or + * write the returned file. + * + * @param name The name of the file to open; can not contain path + * separators. + * @param mode Operating mode. + * @return The resulting {@link FileOutputStream}. + * @see #MODE_APPEND + * @see #MODE_PRIVATE + * @see #openFileInput + * @see #fileList + * @see #deleteFile + * @see java.io.FileOutputStream#FileOutputStream(String) + */ + public abstract FileOutputStream openFileOutput(String name, @FileMode int mode) + throws FileNotFoundException; + + /** + * Delete the given private file associated with this Context's + * application package. + * + * @param name The name of the file to delete; can not contain path + * separators. + * + * @return {@code true} if the file was successfully deleted; else + * {@code false}. + * + * @see #openFileInput + * @see #openFileOutput + * @see #fileList + * @see java.io.File#delete() + */ + public abstract boolean deleteFile(String name); + + /** + * Returns the absolute path on the filesystem where a file created with + * {@link #openFileOutput} is stored. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * + * @param name The name of the file for which you would like to get + * its path. + * + * @return An absolute path to the given file. + * + * @see #openFileOutput + * @see #getFilesDir + * @see #getDir + */ + public abstract File getFileStreamPath(String name); + + /** + * Returns the absolute path on the filesystem where a file created with + * {@link #getSharedPreferences(String, int)} is stored. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * + * @param name The name of the shared preferences for which you would like + * to get a path. + * @return An absolute path to the given file. + * @see #getSharedPreferences(String, int) + * @removed + */ + @SuppressWarnings("HiddenAbstractMethod") + public abstract File getSharedPreferencesPath(String name); + + /** + * Returns the absolute path to the directory on the filesystem where all + * private files belonging to this app are stored. Apps should not use this + * path directly; they should instead use {@link #getFilesDir()}, + * {@link #getCacheDir()}, {@link #getDir(String, int)}, or other storage + * APIs on this class. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. + * + * @see ApplicationInfo#dataDir + */ + public abstract File getDataDir(); + + /** + * Returns the absolute path to the directory on the filesystem where files + * created with {@link #openFileOutput} are stored. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. + * + * @return The path of the directory holding application files. + * @see #openFileOutput + * @see #getFileStreamPath + * @see #getDir + */ + public abstract File getFilesDir(); + + /** + * Returns the absolute path to the directory that is related to the crate on the filesystem. + *

+ * The crateId require a validated file name. It can't contain any "..", ".", + * {@link File#separatorChar} etc.. + *

+ *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. + *

+ * + * @param crateId the relative validated file name under {@link Context#getDataDir()}/crates + * @return the crate directory file. + * @hide + */ + @NonNull + @TestApi + public File getCrateDir(@NonNull String crateId) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Returns the absolute path to the directory on the filesystem similar to + * {@link #getFilesDir()}. The difference is that files placed under this + * directory will be excluded from automatic backup to remote storage. See + * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion + * of the automatic backup mechanism in Android. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. + * + * @return The path of the directory holding application files that will not + * be automatically backed up to remote storage. + * @see #openFileOutput + * @see #getFileStreamPath + * @see #getDir + * @see android.app.backup.BackupAgent + */ + public abstract File getNoBackupFilesDir(); + + /** + * Returns the absolute path to the directory on the primary shared/external + * storage device where the application can place persistent files it owns. + * These files are internal to the applications, and not typically visible + * to the user as media. + *

+ * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: + *

    + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. + *
+ *

+ * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directories returned + * by {@link #getFilesDir()}, etc. + *

+ * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions + * are required to read or write to the returned path; it's always + * accessible to the calling app. This only applies to paths generated for + * package name of the calling application. To access paths belonging to + * other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + *

+ * On devices with multiple users (as described by {@link UserManager}), + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. + *

+ * The returned path may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. + *

+ * Here is an example of typical code to manipulate a file in an + * application's shared storage: + *

+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * private_file} + *

+ * If you supply a non-null type to this function, the returned + * file will be a path to a sub-directory of the given type. Though these + * files are not automatically scanned by the media scanner, you can + * explicitly add them to the media database with + * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], android.media.MediaScannerConnection.OnScanCompletedListener) + * MediaScannerConnection.scanFile}. Note that this is not the same as + * {@link android.os.Environment#getExternalStoragePublicDirectory + * Environment.getExternalStoragePublicDirectory()}, which provides + * directories of media shared by all applications. The directories returned + * here are owned by the application, and their contents will be removed + * when the application is uninstalled. Unlike + * {@link android.os.Environment#getExternalStoragePublicDirectory + * Environment.getExternalStoragePublicDirectory()}, the directory returned + * here will be automatically created for you. + *

+ * Here is an example of typical code to manipulate a picture in an + * application's shared storage and add it to the media database: + *

+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * private_picture} + * + * @param type The type of files directory to return. May be {@code null} + * for the root of the files directory or one of the following + * constants for a subdirectory: + * {@link android.os.Environment#DIRECTORY_MUSIC}, + * {@link android.os.Environment#DIRECTORY_PODCASTS}, + * {@link android.os.Environment#DIRECTORY_RINGTONES}, + * {@link android.os.Environment#DIRECTORY_ALARMS}, + * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, + * {@link android.os.Environment#DIRECTORY_PICTURES}, or + * {@link android.os.Environment#DIRECTORY_MOVIES}. + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. + * @see #getFilesDir + * @see #getExternalFilesDirs(String) + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + @Nullable + public abstract File getExternalFilesDir(@Nullable String type); + + /** + * Returns absolute paths to application-specific directories on all + * shared/external storage devices where the application can place + * persistent files it owns. These files are internal to the application, + * and not typically visible to the user as media. + *

+ * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: + *

    + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. + *
+ *

+ * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directories returned + * by {@link #getFilesDir()}, etc. + *

+ * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. + *

+ * An application may store data on any or all of the returned devices. For + * example, an app may choose to store large files on the device with the + * most available space, as measured by {@link StatFs}. + *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. + *

+ * The returned path may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. + * + * @param type The type of files directory to return. May be {@code null} + * for the root of the files directory or one of the following + * constants for a subdirectory: + * {@link android.os.Environment#DIRECTORY_MUSIC}, + * {@link android.os.Environment#DIRECTORY_PODCASTS}, + * {@link android.os.Environment#DIRECTORY_RINGTONES}, + * {@link android.os.Environment#DIRECTORY_ALARMS}, + * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, + * {@link android.os.Environment#DIRECTORY_PICTURES}, or + * {@link android.os.Environment#DIRECTORY_MOVIES}. + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. The first path returned is the same as + * {@link #getExternalFilesDir(String)}. + * @see #getExternalFilesDir(String) + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File[] getExternalFilesDirs(String type); + + /** + * Return the primary shared/external storage directory where this + * application's OBB files (if there are any) can be found. Note if the + * application does not have any OBB files, this directory may not exist. + *

+ * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: + *

    + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. + *
+ *

+ * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions + * are required to read or write to the path that this method returns. + * However, starting from {@link android.os.Build.VERSION_CODES#M}, + * to read the OBB expansion files, you must declare the + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission in the app manifest and ask for + * permission at runtime as follows: + *

+ *

+ * {@code } + *

+ *

+ * Starting from {@link android.os.Build.VERSION_CODES#N}, + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} + * permission is not required, so don’t ask for this + * permission at runtime. To handle both cases, your app must first try to read the OBB file, + * and if it fails, you must request + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission at runtime. + *

+ * + *

+ * The following code snippet shows how to do this: + *

+ * + *
+     * File obb = new File(obb_filename);
+     * boolean open_failed = false;
+     *
+     * try {
+     *     BufferedReader br = new BufferedReader(new FileReader(obb));
+     *     open_failed = false;
+     *     ReadObbFile(br);
+     * } catch (IOException e) {
+     *     open_failed = true;
+     * }
+     *
+     * if (open_failed) {
+     *     // request READ_EXTERNAL_STORAGE permission before reading OBB file
+     *     ReadObbFileWithPermission();
+     * }
+     * 
+ * + * On devices with multiple users (as described by {@link UserManager}), + * multiple users may share the same OBB storage location. Applications + * should ensure that multiple instances running under different users don't + * interfere with each other. + * + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. + * @see #getObbDirs() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File getObbDir(); + + /** + * Returns absolute paths to application-specific directories on all + * shared/external storage devices where the application's OBB files (if + * there are any) can be found. Note if the application does not have any + * OBB files, these directories may not exist. + *

+ * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: + *

    + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. + *
+ *

+ * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. + *

+ * An application may store data on any or all of the returned devices. For + * example, an app may choose to store large files on the device with the + * most available space, as measured by {@link StatFs}. + *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. + * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. The first path returned is the same as + * {@link #getObbDir()} + * @see #getObbDir() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File[] getObbDirs(); + + /** + * Returns the absolute path to the application specific cache directory on + * the filesystem. + *

+ * The system will automatically delete files in this directory as disk + * space is needed elsewhere on the device. The system will always delete + * older files first, as reported by {@link File#lastModified()}. If + * desired, you can exert more control over how files are deleted using + * {@link StorageManager#setCacheBehaviorGroup(File, boolean)} and + * {@link StorageManager#setCacheBehaviorTombstone(File, boolean)}. + *

+ * Apps are strongly encouraged to keep their usage of cache space below the + * quota returned by + * {@link StorageManager#getCacheQuotaBytes(java.util.UUID)}. If your app + * goes above this quota, your cached files will be some of the first to be + * deleted when additional disk space is needed. Conversely, if your app + * stays under this quota, your cached files will be some of the last to be + * deleted when additional disk space is needed. + *

+ * Note that your cache quota will change over time depending on how + * frequently the user interacts with your app, and depending on how much + * system-wide disk space is used. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. + * + * @return The path of the directory holding application cache files. + * @see #openFileOutput + * @see #getFileStreamPath + * @see #getDir + * @see #getExternalCacheDir + */ + public abstract File getCacheDir(); + + /** + * Returns the absolute path to the application specific cache directory on + * the filesystem designed for storing cached code. + *

+ * The system will delete any files stored in this location both when your + * specific application is upgraded, and when the entire platform is + * upgraded. + *

+ * This location is optimal for storing compiled or optimized code generated + * by your application at runtime. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. + * + * @return The path of the directory holding application code cache files. + */ + public abstract File getCodeCacheDir(); + + /** + * Returns absolute path to application-specific directory on the primary + * shared/external storage device where the application can place cache + * files it owns. These files are internal to the application, and not + * typically visible to the user as media. + *

+ * This is like {@link #getCacheDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: + *

    + *
  • The platform does not always monitor the space available in shared + * storage, and thus may not automatically delete these files. Apps should + * always manage the maximum space used in this location. Currently the only + * time files here will be deleted by the platform is when running on + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and + * {@link Environment#isExternalStorageEmulated(File)} returns true. + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. + *
+ *

+ * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), its contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directory returned by + * {@link #getCacheDir()}. + *

+ * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions + * are required to read or write to the returned path; it's always + * accessible to the calling app. This only applies to paths generated for + * package name of the calling application. To access paths belonging to + * other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + *

+ * On devices with multiple users (as described by {@link UserManager}), + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. + *

+ * The returned path may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. + * + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. + * @see #getCacheDir + * @see #getExternalCacheDirs() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + @Nullable + public abstract File getExternalCacheDir(); + + /** + * Returns absolute path to application-specific directory in the preloaded cache. + *

Files stored in the cache directory can be deleted when the device runs low on storage. + * There is no guarantee when these files will be deleted. + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @Nullable + @SystemApi + public abstract File getPreloadsFileCache(); + + /** + * Returns absolute paths to application-specific directories on all + * shared/external storage devices where the application can place cache + * files it owns. These files are internal to the application, and not + * typically visible to the user as media. + *

+ * This is like {@link #getCacheDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: + *

    + *
  • The platform does not always monitor the space available in shared + * storage, and thus may not automatically delete these files. Apps should + * always manage the maximum space used in this location. Currently the only + * time files here will be deleted by the platform is when running on + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and + * {@link Environment#isExternalStorageEmulated(File)} returns true. + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. + *
+ *

+ * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directory returned by + * {@link #getCacheDir()}. + *

+ * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. + *

+ * An application may store data on any or all of the returned devices. For + * example, an app may choose to store large files on the device with the + * most available space, as measured by {@link StatFs}. + *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. + *

+ * The returned paths may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. + * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. The first path returned is the same as + * {@link #getExternalCacheDir()}. + * @see #getExternalCacheDir() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File[] getExternalCacheDirs(); + + /** + * Returns absolute paths to application-specific directories on all + * shared/external storage devices where the application can place media + * files. These files are scanned and made available to other apps through + * {@link MediaStore}. + *

+ * This is like {@link #getExternalFilesDirs} in that these files will be + * deleted when the application is uninstalled, however there are some + * important differences: + *

    + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. + *
+ *

+ * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. + *

+ * An application may store data on any or all of the returned devices. For + * example, an app may choose to store large files on the device with the + * most available space, as measured by {@link StatFs}. + *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. + *

+ * The returned paths may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. + * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + * @deprecated These directories still exist and are scanned, but developers + * are encouraged to migrate to inserting content into a + * {@link MediaStore} collection directly, as any app can + * contribute new media to {@link MediaStore} with no + * permissions required, starting in + * {@link android.os.Build.VERSION_CODES#Q}. + */ + @Deprecated + public abstract File[] getExternalMediaDirs(); + + /** + * Returns an array of strings naming the private files associated with + * this Context's application package. + * + * @return Array of strings naming the private files. + * + * @see #openFileInput + * @see #openFileOutput + * @see #deleteFile + */ + public abstract String[] fileList(); + + /** + * Retrieve, creating if needed, a new directory in which the application + * can place its own custom data files. You can use the returned File + * object to create and access files in this directory. Note that files + * created through a File object will only be accessible by your own + * application; you can only set the mode of the entire directory, not + * of individual files. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. + * + * @param name Name of the directory to retrieve. This is a directory + * that is created as part of your application data. + * @param mode Operating mode. + * + * @return A {@link File} object for the requested directory. The directory + * will have been created if it does not already exist. + * + * @see #openFileOutput(String, int) + */ + public abstract File getDir(String name, @FileMode int mode); + + /** + * Open a new private SQLiteDatabase associated with this Context's + * application package. Create the database file if it doesn't exist. + * + * @param name The name (unique in the application package) of the database. + * @param mode Operating mode. + * @param factory An optional factory class that is called to instantiate a + * cursor when query is called. + * @return The contents of a newly created database with the given name. + * @throws android.database.sqlite.SQLiteException if the database file + * could not be opened. + * @see #MODE_PRIVATE + * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING + * @see #MODE_NO_LOCALIZED_COLLATORS + * @see #deleteDatabase + */ + public abstract SQLiteDatabase openOrCreateDatabase(String name, + @DatabaseMode int mode, CursorFactory factory); + + /** + * Open a new private SQLiteDatabase associated with this Context's + * application package. Creates the database file if it doesn't exist. + *

+ * Accepts input param: a concrete instance of {@link DatabaseErrorHandler} + * to be used to handle corruption when sqlite reports database corruption. + *

+ * + * @param name The name (unique in the application package) of the database. + * @param mode Operating mode. + * @param factory An optional factory class that is called to instantiate a + * cursor when query is called. + * @param errorHandler the {@link DatabaseErrorHandler} to be used when + * sqlite reports database corruption. if null, + * {@link android.database.DefaultDatabaseErrorHandler} is + * assumed. + * @return The contents of a newly created database with the given name. + * @throws android.database.sqlite.SQLiteException if the database file + * could not be opened. + * @see #MODE_PRIVATE + * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING + * @see #MODE_NO_LOCALIZED_COLLATORS + * @see #deleteDatabase + */ + public abstract SQLiteDatabase openOrCreateDatabase(String name, + @DatabaseMode int mode, CursorFactory factory, + @Nullable DatabaseErrorHandler errorHandler); + + /** + * Move an existing database file from the given source storage context to + * this context. This is typically used to migrate data between storage + * locations after an upgrade, such as migrating to device protected + * storage. + *

+ * The database must be closed before being moved. + * + * @param sourceContext The source context which contains the existing + * database to move. + * @param name The name of the database file. + * @return {@code true} if the move was successful or if the database didn't + * exist in the source context, otherwise {@code false}. + * @see #createDeviceProtectedStorageContext() + */ + public abstract boolean moveDatabaseFrom(Context sourceContext, String name); + + /** + * Delete an existing private SQLiteDatabase associated with this Context's + * application package. + * + * @param name The name (unique in the application package) of the + * database. + * + * @return {@code true} if the database was successfully deleted; else {@code false}. + * + * @see #openOrCreateDatabase + */ + public abstract boolean deleteDatabase(String name); + + /** + * Returns the absolute path on the filesystem where a database created with + * {@link #openOrCreateDatabase} is stored. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * + * @param name The name of the database for which you would like to get + * its path. + * + * @return An absolute path to the given database. + * + * @see #openOrCreateDatabase + */ + public abstract File getDatabasePath(String name); + + /** + * Returns an array of strings naming the private databases associated with + * this Context's application package. + * + * @return Array of strings naming the private databases. + * + * @see #openOrCreateDatabase + * @see #deleteDatabase + */ + public abstract String[] databaseList(); + + /** + * @deprecated Use {@link android.app.WallpaperManager#getDrawable + * WallpaperManager.get()} instead. + */ + @Deprecated + public abstract Drawable getWallpaper(); + + /** + * @deprecated Use {@link android.app.WallpaperManager#peekDrawable + * WallpaperManager.peek()} instead. + */ + @Deprecated + public abstract Drawable peekWallpaper(); + + /** + * @deprecated Use {@link android.app.WallpaperManager#getDesiredMinimumWidth() + * WallpaperManager.getDesiredMinimumWidth()} instead. + */ + @Deprecated + public abstract int getWallpaperDesiredMinimumWidth(); + + /** + * @deprecated Use {@link android.app.WallpaperManager#getDesiredMinimumHeight() + * WallpaperManager.getDesiredMinimumHeight()} instead. + */ + @Deprecated + public abstract int getWallpaperDesiredMinimumHeight(); + + /** + * @deprecated Use {@link android.app.WallpaperManager#setBitmap(Bitmap) + * WallpaperManager.set()} instead. + *

This method requires the caller to hold the permission + * {@link android.Manifest.permission#SET_WALLPAPER}. + */ + @Deprecated + public abstract void setWallpaper(Bitmap bitmap) throws IOException; + + /** + * @deprecated Use {@link android.app.WallpaperManager#setStream(InputStream) + * WallpaperManager.set()} instead. + *

This method requires the caller to hold the permission + * {@link android.Manifest.permission#SET_WALLPAPER}. + */ + @Deprecated + public abstract void setWallpaper(InputStream data) throws IOException; + + /** + * @deprecated Use {@link android.app.WallpaperManager#clear + * WallpaperManager.clear()} instead. + *

This method requires the caller to hold the permission + * {@link android.Manifest.permission#SET_WALLPAPER}. + */ + @Deprecated + public abstract void clearWallpaper() throws IOException; + + /** + * Same as {@link #startActivity(Intent, Bundle)} with no options + * specified. + * + * @param intent The description of the activity to start. + * + * @throws ActivityNotFoundException   + *` + * @see #startActivity(Intent, Bundle) + * @see PackageManager#resolveActivity + */ + public abstract void startActivity(@RequiresPermission Intent intent); + + /** + * Version of {@link #startActivity(Intent)} that allows you to specify the + * user the activity will be started for. This is not available to applications + * that are not pre-installed on the system image. + * @param intent The description of the activity to start. + * @param user The UserHandle of the user to start this activity for. + * @throws ActivityNotFoundException   + * @hide + */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + @SystemApi + public void startActivityAsUser(@RequiresPermission @NonNull Intent intent, + @NonNull UserHandle user) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Launch a new activity. You will not receive any information about when + * the activity exits. + * + *

Note that if this method is being called from outside of an + * {@link android.app.Activity} Context, then the Intent must include + * the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag. This is because, + * without being started from an existing Activity, there is no existing + * task in which to place the new activity and thus it needs to be placed + * in its own separate task. + * + *

This method throws {@link ActivityNotFoundException} + * if there was no Activity found to run the given Intent. + * + * @param intent The description of the activity to start. + * @param options Additional options for how the Activity should be started. + * May be null if there are no options. See {@link android.app.ActivityOptions} + * for how to build the Bundle supplied here; there are no supported definitions + * for building it manually. + * + * @throws ActivityNotFoundException   + * + * @see #startActivity(Intent) + * @see PackageManager#resolveActivity + */ + public abstract void startActivity(@RequiresPermission Intent intent, + @Nullable Bundle options); + + /** + * Version of {@link #startActivity(Intent, Bundle)} that allows you to specify the + * user the activity will be started for. This is not available to applications + * that are not pre-installed on the system image. + * @param intent The description of the activity to start. + * @param options Additional options for how the Activity should be started. + * May be null if there are no options. See {@link android.app.ActivityOptions} + * for how to build the Bundle supplied here; there are no supported definitions + * for building it manually. + * @param userId The UserHandle of the user to start this activity for. + * @throws ActivityNotFoundException   + * @hide + */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + @UnsupportedAppUsage + public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options, + UserHandle userId) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Version of {@link #startActivity(Intent, Bundle)} that returns a result to the caller. This + * is only supported for Views and Fragments. + * @param who The identifier for the calling element that will receive the result. + * @param intent The intent to start. + * @param requestCode The code that will be returned with onActivityResult() identifying this + * request. + * @param options Additional options for how the Activity should be started. + * May be null if there are no options. See {@link android.app.ActivityOptions} + * for how to build the Bundle supplied here; there are no supported definitions + * for building it manually. + * @hide + */ + @UnsupportedAppUsage + public void startActivityForResult( + @NonNull String who, Intent intent, int requestCode, @Nullable Bundle options) { + throw new RuntimeException("This method is only implemented for Activity-based Contexts. " + + "Check canStartActivityForResult() before calling."); + } + + /** + * Identifies whether this Context instance will be able to process calls to + * {@link #startActivityForResult(String, Intent, int, Bundle)}. + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public boolean canStartActivityForResult() { + return false; + } + + /** + * Same as {@link #startActivities(Intent[], Bundle)} with no options + * specified. + * + * @param intents An array of Intents to be started. + * + * @throws ActivityNotFoundException   + * + * @see #startActivities(Intent[], Bundle) + * @see PackageManager#resolveActivity + */ + public abstract void startActivities(@RequiresPermission Intent[] intents); + + /** + * Launch multiple new activities. This is generally the same as calling + * {@link #startActivity(Intent)} for the first Intent in the array, + * that activity during its creation calling {@link #startActivity(Intent)} + * for the second entry, etc. Note that unlike that approach, generally + * none of the activities except the last in the array will be created + * at this point, but rather will be created when the user first visits + * them (due to pressing back from the activity on top). + * + *

This method throws {@link ActivityNotFoundException} + * if there was no Activity found for any given Intent. In this + * case the state of the activity stack is undefined (some Intents in the + * list may be on it, some not), so you probably want to avoid such situations. + * + * @param intents An array of Intents to be started. + * @param options Additional options for how the Activity should be started. + * See {@link android.content.Context#startActivity(Intent, Bundle)} + * Context.startActivity(Intent, Bundle)} for more details. + * + * @throws ActivityNotFoundException   + * + * @see #startActivities(Intent[]) + * @see PackageManager#resolveActivity + */ + public abstract void startActivities(@RequiresPermission Intent[] intents, Bundle options); + + /** + * @hide + * Launch multiple new activities. This is generally the same as calling + * {@link #startActivity(Intent)} for the first Intent in the array, + * that activity during its creation calling {@link #startActivity(Intent)} + * for the second entry, etc. Note that unlike that approach, generally + * none of the activities except the last in the array will be created + * at this point, but rather will be created when the user first visits + * them (due to pressing back from the activity on top). + * + *

This method throws {@link ActivityNotFoundException} + * if there was no Activity found for any given Intent. In this + * case the state of the activity stack is undefined (some Intents in the + * list may be on it, some not), so you probably want to avoid such situations. + * + * @param intents An array of Intents to be started. + * @param options Additional options for how the Activity should be started. + * @param userHandle The user for whom to launch the activities + * See {@link android.content.Context#startActivity(Intent, Bundle)} + * Context.startActivity(Intent, Bundle)} for more details. + * + * @return The corresponding flag {@link ActivityManager#START_CANCELED}, + * {@link ActivityManager#START_SUCCESS} etc. indicating whether the launch was + * successful. + * + * @throws ActivityNotFoundException   + * + * @see #startActivities(Intent[]) + * @see PackageManager#resolveActivity + */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Same as {@link #startIntentSender(IntentSender, Intent, int, int, int, Bundle)} + * with no options specified. + * + * @param intent The IntentSender to launch. + * @param fillInIntent If non-null, this will be provided as the + * intent parameter to {@link IntentSender#sendIntent}. + * @param flagsMask Intent flags in the original IntentSender that you + * would like to change. + * @param flagsValues Desired values for any bits set in + * flagsMask + * @param extraFlags Always set to 0. + * + * @see #startActivity(Intent) + * @see #startIntentSender(IntentSender, Intent, int, int, int, Bundle) + */ + public abstract void startIntentSender(IntentSender intent, @Nullable Intent fillInIntent, + @Intent.MutableFlags int flagsMask, @Intent.MutableFlags int flagsValues, + int extraFlags) throws IntentSender.SendIntentException; + + /** + * Like {@link #startActivity(Intent, Bundle)}, but taking a IntentSender + * to start. If the IntentSender is for an activity, that activity will be started + * as if you had called the regular {@link #startActivity(Intent)} + * here; otherwise, its associated action will be executed (such as + * sending a broadcast) as if you had called + * {@link IntentSender#sendIntent IntentSender.sendIntent} on it. + * + * @param intent The IntentSender to launch. + * @param fillInIntent If non-null, this will be provided as the + * intent parameter to {@link IntentSender#sendIntent}. + * @param flagsMask Intent flags in the original IntentSender that you + * would like to change. + * @param flagsValues Desired values for any bits set in + * flagsMask + * @param extraFlags Always set to 0. + * @param options Additional options for how the Activity should be started. + * See {@link android.content.Context#startActivity(Intent, Bundle)} + * Context.startActivity(Intent, Bundle)} for more details. If options + * have also been supplied by the IntentSender, options given here will + * override any that conflict with those given by the IntentSender. + * + * @see #startActivity(Intent, Bundle) + * @see #startIntentSender(IntentSender, Intent, int, int, int) + */ + public abstract void startIntentSender(IntentSender intent, @Nullable Intent fillInIntent, + @Intent.MutableFlags int flagsMask, @Intent.MutableFlags int flagsValues, + int extraFlags, @Nullable Bundle options) throws IntentSender.SendIntentException; + + /** + * Broadcast the given intent to all interested BroadcastReceivers. This + * call is asynchronous; it returns immediately, and you will continue + * executing while the receivers are run. No results are propagated from + * receivers and receivers can not abort the broadcast. If you want + * to allow receivers to propagate results or abort the broadcast, you must + * send an ordered broadcast using + * {@link #sendOrderedBroadcast(Intent, String)}. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + */ + public abstract void sendBroadcast(@RequiresPermission Intent intent); + + /** + * Broadcast the given intent to all interested BroadcastReceivers, allowing + * an optional required permission to be enforced. This + * call is asynchronous; it returns immediately, and you will continue + * executing while the receivers are run. No results are propagated from + * receivers and receivers can not abort the broadcast. If you want + * to allow receivers to propagate results or abort the broadcast, you must + * send an ordered broadcast using + * {@link #sendOrderedBroadcast(Intent, String)}. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission (optional) String naming a permission that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + */ + public abstract void sendBroadcast(@RequiresPermission Intent intent, + @Nullable String receiverPermission); + + + /** + * Broadcast the given intent to all interested BroadcastReceivers, allowing + * an array of required permissions to be enforced. This call is asynchronous; it returns + * immediately, and you will continue executing while the receivers are run. No results are + * propagated from receivers and receivers can not abort the broadcast. If you want to allow + * receivers to propagate results or abort the broadcast, you must send an ordered broadcast + * using {@link #sendOrderedBroadcast(Intent, String)}. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermissions Array of names of permissions that a receiver must hold + * in order to receive your broadcast. + * If empty, no permissions are required. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + * @hide + */ + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Like {@link #sendBroadcastMultiplePermissions(Intent, String[])}, but also allows + * specification of a list of excluded permissions. This allows sending a broadcast to an + * app that has the permissions in `receiverPermissions` but not `excludedPermissions`. + * @hide + */ + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Version of {@link #sendBroadcastMultiplePermissions(Intent, String[])} that allows you to + * specify the {@link android.app.BroadcastOptions}. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermissions Array of names of permissions that a receiver must hold + * in order to receive your broadcast. + * If empty, no permissions are required. + * @param options Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * @see #sendBroadcastMultiplePermissions(Intent, String[]) + * @see android.app.BroadcastOptions + * @hide + */ + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions, @Nullable Bundle options) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Broadcast the given intent to all interested BroadcastReceivers, allowing + * an array of required permissions to be enforced. This call is asynchronous; it returns + * immediately, and you will continue executing while the receivers are run. No results are + * propagated from receivers and receivers can not abort the broadcast. If you want to allow + * receivers to propagate results or abort the broadcast, you must send an ordered broadcast + * using {@link #sendOrderedBroadcast(Intent, String)}. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermissions Array of names of permissions that a receiver must hold + * in order to receive your broadcast. + * If empty, no permissions are required. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + */ + public void sendBroadcastWithMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions) { + sendBroadcastMultiplePermissions(intent, receiverPermissions); + } + + /** + * Broadcast the given intent to all interested BroadcastReceivers, allowing + * an array of required permissions to be enforced. This call is asynchronous; it returns + * immediately, and you will continue executing while the receivers are run. No results are + * propagated from receivers and receivers can not abort the broadcast. If you want to allow + * receivers to propagate results or abort the broadcast, you must send an ordered broadcast + * using {@link #sendOrderedBroadcast(Intent, String)}. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param user The user to send the broadcast to. + * @param receiverPermissions Array of names of permissions that a receiver must hold + * in order to receive your broadcast. + * If null or empty, no permissions are required. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + public abstract void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, + String[] receiverPermissions); + + /** + * Broadcast the given intent to all interested BroadcastReceivers, allowing + * an optional required permission to be enforced. This + * call is asynchronous; it returns immediately, and you will continue + * executing while the receivers are run. No results are propagated from + * receivers and receivers can not abort the broadcast. If you want + * to allow receivers to propagate results or abort the broadcast, you must + * send an ordered broadcast using + * {@link #sendOrderedBroadcast(Intent, String)}. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission (optional) String naming a permission that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + public abstract void sendBroadcast(Intent intent, + @Nullable String receiverPermission, + @Nullable Bundle options); + + /** + * Like {@link #sendBroadcast(Intent, String)}, but also allows specification + * of an associated app op as per {@link android.app.AppOpsManager}. + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @UnsupportedAppUsage + public abstract void sendBroadcast(Intent intent, + String receiverPermission, int appOp); + + /** + * Broadcast the given intent to all interested BroadcastReceivers, delivering + * them one at a time to allow more preferred receivers to consume the + * broadcast before it is delivered to less preferred receivers. This + * call is asynchronous; it returns immediately, and you will continue + * executing while the receivers are run. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission (optional) String naming a permissions that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + */ + public abstract void sendOrderedBroadcast(@RequiresPermission Intent intent, + @Nullable String receiverPermission); + + /** + * Version of {@link #sendBroadcast(Intent)} that allows you to + * receive data back from the broadcast. This is accomplished by + * supplying your own BroadcastReceiver when calling, which will be + * treated as a final receiver at the end of the broadcast -- its + * {@link BroadcastReceiver#onReceive} method will be called with + * the result values collected from the other receivers. The broadcast will + * be serialized in the same way as calling + * {@link #sendOrderedBroadcast(Intent, String)}. + * + *

Like {@link #sendBroadcast(Intent)}, this method is + * asynchronous; it will return before + * resultReceiver.onReceive() is called. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission String naming a permissions that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param resultReceiver Your own BroadcastReceiver to treat as the final + * receiver of the broadcast. + * @param scheduler A custom Handler with which to schedule the + * resultReceiver callback; if null it will be + * scheduled in the Context's main thread. + * @param initialCode An initial value for the result code. Often + * Activity.RESULT_OK. + * @param initialData An initial value for the result data. Often + * null. + * @param initialExtras An initial value for the result extras. Often + * null. + * + * @see #sendBroadcast(Intent) + * @see #sendBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String) + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see android.app.Activity#RESULT_OK + */ + public abstract void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent, + @Nullable String receiverPermission, @Nullable BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras); + + /** + * Version of {@link #sendBroadcast(Intent)} that allows you to + * receive data back from the broadcast. This is accomplished by + * supplying your own BroadcastReceiver when calling, which will be + * treated as a final receiver at the end of the broadcast -- its + * {@link BroadcastReceiver#onReceive} method will be called with + * the result values collected from the other receivers. The broadcast will + * be serialized in the same way as calling + * {@link #sendOrderedBroadcast(Intent, String)}. + * + *

Like {@link #sendBroadcast(Intent)}, this method is + * asynchronous; it will return before + * resultReceiver.onReceive() is called. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission String naming a permissions that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * @param resultReceiver Your own BroadcastReceiver to treat as the final + * receiver of the broadcast. + * @param scheduler A custom Handler with which to schedule the + * resultReceiver callback; if null it will be + * scheduled in the Context's main thread. + * @param initialCode An initial value for the result code. Often + * Activity.RESULT_OK. + * @param initialData An initial value for the result data. Often + * null. + * @param initialExtras An initial value for the result extras. Often + * null. + * @see #sendBroadcast(Intent) + * @see #sendBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String) + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see android.app.Activity#RESULT_OK + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + public abstract void sendOrderedBroadcast(@NonNull Intent intent, + @Nullable String receiverPermission, @Nullable Bundle options, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras); + + /** + * Like {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, + * int, String, android.os.Bundle)}, but also allows specification + * of an associated app op as per {@link android.app.AppOpsManager}. + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @UnsupportedAppUsage + public abstract void sendOrderedBroadcast(Intent intent, + String receiverPermission, int appOp, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras); + + /** + * Version of {@link #sendBroadcast(Intent)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. + * @param intent The intent to broadcast + * @param user UserHandle to send the intent to. + * @see #sendBroadcast(Intent) + */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user); + + /** + * Version of {@link #sendBroadcast(Intent, String)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param user UserHandle to send the intent to. + * @param receiverPermission (optional) String naming a permission that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * + * @see #sendBroadcast(Intent, String) + */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user, @Nullable String receiverPermission); + + /** + * Version of {@link #sendBroadcast(Intent, String, Bundle)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param user UserHandle to send the intent to. + * @param receiverPermission (optional) String naming a permission that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * + * @see #sendBroadcast(Intent, String, Bundle) + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user, @Nullable String receiverPermission, @Nullable Bundle options); + + /** + * Version of {@link #sendBroadcast(Intent, String)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param user UserHandle to send the intent to. + * @param receiverPermission (optional) String naming a permission that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param appOp The app op associated with the broadcast. + * + * @see #sendBroadcast(Intent, String) + * + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user, @Nullable String receiverPermission, int appOp); + + /** + * Version of + * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)} + * that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param user UserHandle to send the intent to. + * @param receiverPermission String naming a permissions that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param resultReceiver Your own BroadcastReceiver to treat as the final + * receiver of the broadcast. + * @param scheduler A custom Handler with which to schedule the + * resultReceiver callback; if null it will be + * scheduled in the Context's main thread. + * @param initialCode An initial value for the result code. Often + * Activity.RESULT_OK. + * @param initialData An initial value for the result data. Often + * null. + * @param initialExtras An initial value for the result extras. Often + * null. + * + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + public abstract void sendOrderedBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user, @Nullable String receiverPermission, BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras); + + /** + * Similar to above but takes an appOp as well, to enforce restrictions. + * @see #sendOrderedBroadcastAsUser(Intent, UserHandle, String, + * BroadcastReceiver, Handler, int, String, Bundle) + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, + @Nullable String receiverPermission, int appOp, BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras); + + /** + * Similar to above but takes an appOp as well, to enforce restrictions, and an options Bundle. + * @see #sendOrderedBroadcastAsUser(Intent, UserHandle, String, + * BroadcastReceiver, Handler, int, String, Bundle) + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + @UnsupportedAppUsage + public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, + @Nullable String receiverPermission, int appOp, @Nullable Bundle options, + BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, + @Nullable String initialData, @Nullable Bundle initialExtras); + + /** + * Version of + * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, + * Bundle)} that allows you to specify the App Op to enforce restrictions on which receivers + * the broadcast will be sent to. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission String naming a permissions that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param receiverAppOp The app op associated with the broadcast. If null, no appOp is + * required. If both receiverAppOp and receiverPermission are non-null, + * a receiver must have both of them to + * receive the broadcast + * @param resultReceiver Your own BroadcastReceiver to treat as the final + * receiver of the broadcast. + * @param scheduler A custom Handler with which to schedule the + * resultReceiver callback; if null it will be + * scheduled in the Context's main thread. + * @param initialCode An initial value for the result code. Often + * Activity.RESULT_OK. + * @param initialData An initial value for the result data. Often + * null. + * @param initialExtras An initial value for the result extras. Often + * null. + * + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + */ + public void sendOrderedBroadcast(@NonNull Intent intent, @Nullable String receiverPermission, + @Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Version of + * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, + * Bundle)} that allows you to specify the App Op to enforce restrictions on which receivers + * the broadcast will be sent to as well as supply an optional sending options + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission String naming a permissions that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param receiverAppOp The app op associated with the broadcast. If null, no appOp is + * required. If both receiverAppOp and receiverPermission are non-null, + * a receiver must have both of them to + * receive the broadcast + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * @param resultReceiver Your own BroadcastReceiver to treat as the final + * receiver of the broadcast. + * @param scheduler A custom Handler with which to schedule the + * resultReceiver callback; if null it will be + * scheduled in the Context's main thread. + * @param initialCode An initial value for the result code. Often + * Activity.RESULT_OK. + * @param initialData An initial value for the result data. Often + * null. + * @param initialExtras An initial value for the result extras. Often + * null. + * + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + * @see android.app.BroadcastOptions + * @hide + */ + public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent, int initialCode, + @Nullable String receiverPermission, @Nullable String receiverAppOp, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + @Nullable String initialData, @Nullable Bundle initialExtras, + @Nullable Bundle options) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + *

Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the + * Intent you are sending stays around after the broadcast is complete, + * so that others can quickly retrieve that data through the return + * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In + * all other ways, this behaves the same as + * {@link #sendBroadcast(Intent)}. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that something + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast, and the Intent will be held to + * be re-broadcast to future receivers. + * + * @see #sendBroadcast(Intent) + * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) + public abstract void sendStickyBroadcast(@RequiresPermission Intent intent); + + /** + *

Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the + * Intent you are sending stays around after the broadcast is complete, + * so that others can quickly retrieve that data through the return + * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In + * all other ways, this behaves the same as + * {@link #sendBroadcast(Intent)}. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that something + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast, and the Intent will be held to + * be re-broadcast to future receivers. + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * + * @see #sendBroadcast(Intent) + * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) + public void sendStickyBroadcast(@RequiresPermission @NonNull Intent intent, + @Nullable Bundle options) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + *

Version of {@link #sendStickyBroadcast} that allows you to + * receive data back from the broadcast. This is accomplished by + * supplying your own BroadcastReceiver when calling, which will be + * treated as a final receiver at the end of the broadcast -- its + * {@link BroadcastReceiver#onReceive} method will be called with + * the result values collected from the other receivers. The broadcast will + * be serialized in the same way as calling + * {@link #sendOrderedBroadcast(Intent, String)}. + * + *

Like {@link #sendBroadcast(Intent)}, this method is + * asynchronous; it will return before + * resultReceiver.onReceive() is called. Note that the sticky data + * stored is only the data you initially supply to the broadcast, not + * the result of any changes made by the receivers. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that something + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param resultReceiver Your own BroadcastReceiver to treat as the final + * receiver of the broadcast. + * @param scheduler A custom Handler with which to schedule the + * resultReceiver callback; if null it will be + * scheduled in the Context's main thread. + * @param initialCode An initial value for the result code. Often + * Activity.RESULT_OK. + * @param initialData An initial value for the result data. Often + * null. + * @param initialExtras An initial value for the result extras. Often + * null. + * + * @see #sendBroadcast(Intent) + * @see #sendBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendStickyBroadcast(Intent) + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see android.app.Activity#RESULT_OK + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) + public abstract void sendStickyOrderedBroadcast(@RequiresPermission Intent intent, + BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras); + + /** + *

Remove the data previously sent with {@link #sendStickyBroadcast}, + * so that it is as if the sticky broadcast had never happened. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that something + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent that was previously broadcast. + * + * @see #sendStickyBroadcast + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) + public abstract void removeStickyBroadcast(@RequiresPermission Intent intent); + + /** + *

Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that something + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast, and the Intent will be held to + * be re-broadcast to future receivers. + * @param user UserHandle to send the intent to. + * + * @see #sendBroadcast(Intent) + */ + @Deprecated + @RequiresPermission(allOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.BROADCAST_STICKY + }) + public abstract void sendStickyBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user); + + /** + * @hide + * This is just here for sending CONNECTIVITY_ACTION. + */ + @SuppressWarnings("HiddenAbstractMethod") + @Deprecated + @RequiresPermission(allOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.BROADCAST_STICKY + }) + public abstract void sendStickyBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user, Bundle options); + + /** + *

Version of + * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)} + * that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that something + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param user UserHandle to send the intent to. + * @param resultReceiver Your own BroadcastReceiver to treat as the final + * receiver of the broadcast. + * @param scheduler A custom Handler with which to schedule the + * resultReceiver callback; if null it will be + * scheduled in the Context's main thread. + * @param initialCode An initial value for the result code. Often + * Activity.RESULT_OK. + * @param initialData An initial value for the result data. Often + * null. + * @param initialExtras An initial value for the result extras. Often + * null. + * + * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) + */ + @Deprecated + @RequiresPermission(allOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.BROADCAST_STICKY + }) + public abstract void sendStickyOrderedBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user, BroadcastReceiver resultReceiver, + @Nullable Handler scheduler, int initialCode, @Nullable String initialData, + @Nullable Bundle initialExtras); + + /** + *

Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. + * + *

You must hold the {@link android.Manifest.permission#BROADCAST_STICKY} + * permission in order to use this API. If you do not hold that + * permission, {@link SecurityException} will be thrown. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that something + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent that was previously broadcast. + * @param user UserHandle to remove the sticky broadcast from. + * + * @see #sendStickyBroadcastAsUser + */ + @Deprecated + @RequiresPermission(allOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.BROADCAST_STICKY + }) + public abstract void removeStickyBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user); + + /** + * Register a BroadcastReceiver to be run in the main activity thread. The + * receiver will be called with any broadcast Intent that + * matches filter, in the main application thread. + * + *

The system may broadcast Intents that are "sticky" -- these stay + * around after the broadcast has finished, to be sent to any later + * registrations. If your IntentFilter matches one of these sticky + * Intents, that Intent will be returned by this function + * and sent to your receiver as if it had just + * been broadcast. + * + *

There may be multiple sticky Intents that match filter, + * in which case each of these will be sent to receiver. In + * this case, only one of these can be returned directly by the function; + * which of these that is returned is arbitrarily decided by the system. + * + *

If you know the Intent your are registering for is sticky, you can + * supply null for your receiver. In this case, no receiver is + * registered -- the function simply returns the sticky Intent that + * matches filter. In the case of multiple matches, the same + * rules as described above apply. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + *

As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers + * registered with this method will correctly respect the + * {@link Intent#setPackage(String)} specified for an Intent being broadcast. + * Prior to that, it would be ignored and delivered to all matching registered + * receivers. Be careful if using this for security.

+ * + *

Note: this method cannot be called from a + * {@link BroadcastReceiver} component; that is, from a BroadcastReceiver + * that is declared in an application's manifest. It is okay, however, to call + * this method from another BroadcastReceiver that has itself been registered + * at run time with {@link #registerReceiver}, since the lifetime of such a + * registered BroadcastReceiver is tied to the object that registered it.

+ * + * @param receiver The BroadcastReceiver to handle the broadcast. + * @param filter Selects the Intent broadcasts to be received. + * + * @return The first sticky intent found that matches filter, + * or null if there are none. + * + * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) + * @see #sendBroadcast + * @see #unregisterReceiver + */ + @Nullable + public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver, + IntentFilter filter); + + /** + * Register to receive intent broadcasts, with the receiver optionally being + * exposed to Instant Apps. See + * {@link #registerReceiver(BroadcastReceiver, IntentFilter)} for more + * information. By default Instant Apps cannot interact with receivers in other + * applications, this allows you to expose a receiver that Instant Apps can + * interact with. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + *

As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers + * registered with this method will correctly respect the + * {@link Intent#setPackage(String)} specified for an Intent being broadcast. + * Prior to that, it would be ignored and delivered to all matching registered + * receivers. Be careful if using this for security.

+ * + * @param receiver The BroadcastReceiver to handle the broadcast. + * @param filter Selects the Intent broadcasts to be received. + * @param flags Additional options for the receiver. May be 0 or + * {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. + * + * @return The first sticky intent found that matches filter, + * or null if there are none. + * + * @see #registerReceiver(BroadcastReceiver, IntentFilter) + * @see #sendBroadcast + * @see #unregisterReceiver + */ + @Nullable + public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver, + IntentFilter filter, + @RegisterReceiverFlags int flags); + + /** + * Register to receive intent broadcasts, to run in the context of + * scheduler. See + * {@link #registerReceiver(BroadcastReceiver, IntentFilter)} for more + * information. This allows you to enforce permissions on who can + * broadcast intents to your receiver, or have the receiver run in + * a different thread than the main application thread. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + *

As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers + * registered with this method will correctly respect the + * {@link Intent#setPackage(String)} specified for an Intent being broadcast. + * Prior to that, it would be ignored and delivered to all matching registered + * receivers. Be careful if using this for security.

+ * + * @param receiver The BroadcastReceiver to handle the broadcast. + * @param filter Selects the Intent broadcasts to be received. + * @param broadcastPermission String naming a permissions that a + * broadcaster must hold in order to send an Intent to you. If null, + * no permission is required. + * @param scheduler Handler identifying the thread that will receive + * the Intent. If null, the main thread of the process will be used. + * + * @return The first sticky intent found that matches filter, + * or null if there are none. + * + * @see #registerReceiver(BroadcastReceiver, IntentFilter) + * @see #sendBroadcast + * @see #unregisterReceiver + */ + @Nullable + public abstract Intent registerReceiver(BroadcastReceiver receiver, + IntentFilter filter, @Nullable String broadcastPermission, + @Nullable Handler scheduler); + + /** + * Register to receive intent broadcasts, to run in the context of + * scheduler. See + * {@link #registerReceiver(BroadcastReceiver, IntentFilter, int)} and + * {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)} + * for more information. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + *

As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers + * registered with this method will correctly respect the + * {@link Intent#setPackage(String)} specified for an Intent being broadcast. + * Prior to that, it would be ignored and delivered to all matching registered + * receivers. Be careful if using this for security.

+ * + * @param receiver The BroadcastReceiver to handle the broadcast. + * @param filter Selects the Intent broadcasts to be received. + * @param broadcastPermission String naming a permissions that a + * broadcaster must hold in order to send an Intent to you. If null, + * no permission is required. + * @param scheduler Handler identifying the thread that will receive + * the Intent. If null, the main thread of the process will be used. + * @param flags Additional options for the receiver. May be 0 or + * {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. + * + * @return The first sticky intent found that matches filter, + * or null if there are none. + * + * @see #registerReceiver(BroadcastReceiver, IntentFilter, int) + * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) + * @see #sendBroadcast + * @see #unregisterReceiver + */ + @Nullable + public abstract Intent registerReceiver(BroadcastReceiver receiver, + IntentFilter filter, @Nullable String broadcastPermission, + @Nullable Handler scheduler, @RegisterReceiverFlags int flags); + + /** + * Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)} + * but this receiver will receive broadcasts that are sent to all users. The receiver can + * use {@link BroadcastReceiver#getSendingUser} to determine on which user the broadcast + * was sent. + * + * @param receiver The BroadcastReceiver to handle the broadcast. + * @param filter Selects the Intent broadcasts to be received. + * @param broadcastPermission String naming a permissions that a + * broadcaster must hold in order to send an Intent to you. If {@code null}, + * no permission is required. + * @param scheduler Handler identifying the thread that will receive + * the Intent. If {@code null}, the main thread of the process will be used. + * + * @return The first sticky intent found that matches filter, + * or {@code null} if there are none. + * + * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) + * @see #sendBroadcast + * @see #unregisterReceiver + * @hide + */ + @Nullable + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + @SystemApi + public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver, + @NonNull IntentFilter filter, @Nullable String broadcastPermission, + @Nullable Handler scheduler) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * @hide + * Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) + * but for a specific user. This receiver will receiver broadcasts that + * are sent to the requested user. + * + * @param receiver The BroadcastReceiver to handle the broadcast. + * @param user UserHandle to send the intent to. + * @param filter Selects the Intent broadcasts to be received. + * @param broadcastPermission String naming a permissions that a + * broadcaster must hold in order to send an Intent to you. If null, + * no permission is required. + * @param scheduler Handler identifying the thread that will receive + * the Intent. If null, the main thread of the process will be used. + * + * @return The first sticky intent found that matches filter, + * or null if there are none. + * + * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) + * @see #sendBroadcast + * @see #unregisterReceiver + */ + @SuppressWarnings("HiddenAbstractMethod") + @Nullable + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + @UnsupportedAppUsage + public abstract Intent registerReceiverAsUser(BroadcastReceiver receiver, + UserHandle user, IntentFilter filter, @Nullable String broadcastPermission, + @Nullable Handler scheduler); + + /** + * Unregister a previously registered BroadcastReceiver. All + * filters that have been registered for this BroadcastReceiver will be + * removed. + * + * @param receiver The BroadcastReceiver to unregister. + * + * @see #registerReceiver + */ + public abstract void unregisterReceiver(BroadcastReceiver receiver); + + /** + * Request that a given application service be started. The Intent + * should either contain the complete class name of a specific service + * implementation to start, or a specific package name to target. If the + * Intent is less specified, it logs a warning about this. In this case any of the + * multiple matching services may be used. If this service + * is not already running, it will be instantiated and started (creating a + * process for it if needed); if it is running then it remains running. + * + *

Every call to this method will result in a corresponding call to + * the target service's {@link android.app.Service#onStartCommand} method, + * with the intent given here. This provides a convenient way + * to submit jobs to a service without having to bind and call on to its + * interface. + * + *

Using startService() overrides the default service lifetime that is + * managed by {@link #bindService}: it requires the service to remain + * running until {@link #stopService} is called, regardless of whether + * any clients are connected to it. Note that calls to startService() + * do not nest: no matter how many times you call startService(), + * a single call to {@link #stopService} will stop it. + * + *

The system attempts to keep running services around as much as + * possible. The only time they should be stopped is if the current + * foreground application is using so many resources that the service needs + * to be killed. If any errors happen in the service's process, it will + * automatically be restarted. + * + *

This function will throw {@link SecurityException} if you do not + * have permission to start the given service. + * + *

+ *

Note: Each call to startService() + * results in significant work done by the system to manage service + * lifecycle surrounding the processing of the intent, which can take + * multiple milliseconds of CPU time. Due to this cost, startService() + * should not be used for frequent intent delivery to a service, and only + * for scheduling significant work. Use {@link #bindService bound services} + * for high frequency calls. + *

+ * + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#O}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#O} + * or higher are not allowed to start background services from the background. + * See + * + * Background Execution Limits + * for more details. + * + *

Note: + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * + * Behavior changes: Apps targeting Android 12 + * + * for more details. + *

+ * + * @param service Identifies the service to be started. The Intent must be + * fully explicit (supplying a component name). Additional values + * may be included in the Intent extras to supply arguments along with + * this specific start call. + * + * @return If the service is being started or is already running, the + * {@link ComponentName} of the actual service that was started is + * returned; else if the service does not exist null is returned. + * + * @throws SecurityException If the caller does not have permission to access the service + * or the service can not be found. + * @throws IllegalStateException + * Before Android {@link android.os.Build.VERSION_CODES#S}, + * if the application is in a state where the service + * can not be started (such as not in the foreground in a state when services are allowed), + * {@link IllegalStateException} was thrown. + * @throws android.app.BackgroundServiceStartNotAllowedException + * On Android {@link android.os.Build.VERSION_CODES#S} and later, + * if the application is in a state where the service + * can not be started (such as not in the foreground in a state when services are allowed), + * {@link android.app.BackgroundServiceStartNotAllowedException} is thrown + * This excemption extends {@link IllegalStateException}, so apps can + * use {@code catch (IllegalStateException)} to catch both. + * + * @see #startForegroundService(Intent) + * @see #stopService + * @see #bindService + */ + @Nullable + public abstract ComponentName startService(Intent service); + + /** + * Similar to {@link #startService(Intent)}, but with an implicit promise that the + * Service will call {@link android.app.Service#startForeground(int, android.app.Notification) + * startForeground(int, android.app.Notification)} once it begins running. The service is given + * an amount of time comparable to the ANR interval to do this, otherwise the system + * will automatically stop the service and declare the app ANR. + * + *

Unlike the ordinary {@link #startService(Intent)}, this method can be used + * at any time, regardless of whether the app hosting the service is in a foreground + * state. + * + *

+ *

Note: + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * + * Behavior changes: Apps targeting Android 12 + * + * for more details. + *

+ * + * @param service Identifies the service to be started. The Intent must be + * fully explicit (supplying a component name). Additional values + * may be included in the Intent extras to supply arguments along with + * this specific start call. + * + * @return If the service is being started or is already running, the + * {@link ComponentName} of the actual service that was started is + * returned; else if the service does not exist null is returned. + * + * @throws SecurityException If the caller does not have permission to access the service + * or the service can not be found. + * + * @throws android.app.ForegroundServiceStartNotAllowedException + * If the caller app's targeting API is + * {@link android.os.Build.VERSION_CODES#S} or later, and the foreground service is restricted + * from start due to background restriction. + * + * @see #stopService + * @see android.app.Service#startForeground(int, android.app.Notification) + */ + @Nullable + public abstract ComponentName startForegroundService(Intent service); + + /** + * @hide like {@link #startForegroundService(Intent)} but for a specific user. + */ + @SuppressWarnings("HiddenAbstractMethod") + @Nullable + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user); + + /** + * Request that a given application service be stopped. If the service is + * not running, nothing happens. Otherwise it is stopped. Note that calls + * to startService() are not counted -- this stops the service no matter + * how many times it was started. + * + *

Note that if a stopped service still has {@link ServiceConnection} + * objects bound to it with the {@link #BIND_AUTO_CREATE} set, it will + * not be destroyed until all of these bindings are removed. See + * the {@link android.app.Service} documentation for more details on a + * service's lifecycle. + * + *

This function will throw {@link SecurityException} if you do not + * have permission to stop the given service. + * + * @param service Description of the service to be stopped. The Intent must be either + * fully explicit (supplying a component name) or specify a specific package + * name it is targeted to. + * + * @return If there is a service matching the given Intent that is already + * running, then it is stopped and {@code true} is returned; else {@code false} is returned. + * + * @throws SecurityException If the caller does not have permission to access the service + * or the service can not be found. + * @throws IllegalStateException If the application is in a state where the service + * can not be started (such as not in the foreground in a state when services are allowed). + * + * @see #startService + */ + public abstract boolean stopService(Intent service); + + /** + * @hide like {@link #startService(Intent)} but for a specific user. + */ + @SuppressWarnings("HiddenAbstractMethod") + @Nullable + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + @UnsupportedAppUsage + public abstract ComponentName startServiceAsUser(Intent service, UserHandle user); + + /** + * @hide like {@link #stopService(Intent)} but for a specific user. + */ + @SuppressWarnings("HiddenAbstractMethod") + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + public abstract boolean stopServiceAsUser(Intent service, UserHandle user); + + /** + * Connect to an application service, creating it if needed. This defines + * a dependency between your application and the service. The given + * conn will receive the service object when it is created and be + * told if it dies and restarts. The service will be considered required + * by the system only for as long as the calling context exists. For + * example, if this Context is an Activity that is stopped, the service will + * not be required to continue running until the Activity is resumed. + * + *

If the service does not support binding, it may return {@code null} from + * its {@link android.app.Service#onBind(Intent) onBind()} method. If it does, then + * the ServiceConnection's + * {@link ServiceConnection#onNullBinding(ComponentName) onNullBinding()} method + * will be invoked instead of + * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder) onServiceConnected()}. + * + *

This method will throw {@link SecurityException} if the calling app does not + * have permission to bind to the given service. + * + *

Note: this method cannot be called from a + * {@link BroadcastReceiver} component. A pattern you can use to + * communicate from a BroadcastReceiver to a Service is to call + * {@link #startService} with the arguments containing the command to be + * sent, with the service calling its + * {@link android.app.Service#stopSelf(int)} method when done executing + * that command. See the API demo App/Service/Service Start Arguments + * Controller for an illustration of this. It is okay, however, to use + * this method from a BroadcastReceiver that has been registered with + * {@link #registerReceiver}, since the lifetime of this BroadcastReceiver + * is tied to another object (the one that registered it).

+ * + * @param service Identifies the service to connect to. The Intent must + * specify an explicit component name. + * @param conn Receives information as the service is started and stopped. + * This must be a valid ServiceConnection object; it must not be null. + * @param flags Operation options for the binding. May be 0, + * {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND}, + * {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT}, + * {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}. + * {@link #BIND_IMPORTANT}, {@link #BIND_ADJUST_WITH_ACTIVITY}, + * {@link #BIND_NOT_PERCEPTIBLE}, or {@link #BIND_INCLUDE_CAPABILITIES}. + * @return {@code true} if the system is in the process of bringing up a + * service that your client has permission to bind to; {@code false} + * if the system couldn't find the service or if your client doesn't + * have permission to bind to it. If this value is {@code true}, you + * should later call {@link #unbindService} to release the + * connection. + * + * @throws SecurityException If the caller does not have permission to access the service + * or the service can not be found. + * + * @see #unbindService + * @see #startService + * @see #BIND_AUTO_CREATE + * @see #BIND_DEBUG_UNBIND + * @see #BIND_NOT_FOREGROUND + * @see #BIND_ABOVE_CLIENT + * @see #BIND_ALLOW_OOM_MANAGEMENT + * @see #BIND_WAIVE_PRIORITY + * @see #BIND_IMPORTANT + * @see #BIND_ADJUST_WITH_ACTIVITY + * @see #BIND_NOT_PERCEPTIBLE + * @see #BIND_INCLUDE_CAPABILITIES + */ + public abstract boolean bindService(@RequiresPermission Intent service, + @NonNull ServiceConnection conn, @BindServiceFlags int flags); + + /** + * Same as {@link #bindService(Intent, ServiceConnection, int)} with executor to control + * ServiceConnection callbacks. + * @param executor Callbacks on ServiceConnection will be called on executor. Must use same + * instance for the same instance of ServiceConnection. + */ + public boolean bindService(@RequiresPermission @NonNull Intent service, + @BindServiceFlags int flags, @NonNull @CallbackExecutor Executor executor, + @NonNull ServiceConnection conn) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Variation of {@link #bindService} that, in the specific case of isolated + * services, allows the caller to generate multiple instances of a service + * from a single component declaration. In other words, you can use this to bind + * to a service that has specified {@link android.R.attr#isolatedProcess} and, in + * addition to the existing behavior of running in an isolated process, you can + * also through the arguments here have the system bring up multiple concurrent + * processes hosting their own instances of that service. The instanceName + * you provide here identifies the different instances, and you can use + * {@link #updateServiceGroup(ServiceConnection, int, int)} to tell the system how it + * should manage each of these instances. + * + * @param service Identifies the service to connect to. The Intent must + * specify an explicit component name. + * @param flags Operation options for the binding as per {@link #bindService}. + * @param instanceName Unique identifier for the service instance. Each unique + * name here will result in a different service instance being created. Identifiers + * must only contain ASCII letters, digits, underscores, and periods. + * @return Returns success of binding as per {@link #bindService}. + * @param executor Callbacks on ServiceConnection will be called on executor. + * Must use same instance for the same instance of ServiceConnection. + * @param conn Receives information as the service is started and stopped. + * This must be a valid ServiceConnection object; it must not be null. + * + * @throws SecurityException If the caller does not have permission to access the service + * @throws IllegalArgumentException If the instanceName is invalid. + * + * @see #bindService + * @see #updateServiceGroup + * @see android.R.attr#isolatedProcess + */ + public boolean bindIsolatedService(@RequiresPermission @NonNull Intent service, + @BindServiceFlags int flags, @NonNull String instanceName, + @NonNull @CallbackExecutor Executor executor, @NonNull ServiceConnection conn) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Binds to a service in the given {@code user} in the same manner as + * {@link #bindService(Intent, ServiceConnection, int)}. + * + *

If the given {@code user} is in the same profile group and the target package is the + * same as the caller, {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} is + * sufficient. Otherwise, requires {@code android.Manifest.permission.INTERACT_ACROSS_USERS} + * for interacting with other users. + * + * @param service Identifies the service to connect to. The Intent must + * specify an explicit component name. + * @param conn Receives information as the service is started and stopped. + * This must be a valid ServiceConnection object; it must not be null. + * @param flags Operation options for the binding. May be 0, + * {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND}, + * {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT}, + * {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}. + * {@link #BIND_IMPORTANT}, or + * {@link #BIND_ADJUST_WITH_ACTIVITY}. + * @return {@code true} if the system is in the process of bringing up a + * service that your client has permission to bind to; {@code false} + * if the system couldn't find the service. If this value is {@code true}, you + * should later call {@link #unbindService} to release the + * connection. + * + * @throws SecurityException if the client does not have the required permission to bind. + */ + @SuppressWarnings("unused") + @RequiresPermission(anyOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.INTERACT_ACROSS_PROFILES + }) + public boolean bindServiceAsUser( + @NonNull @RequiresPermission Intent service, @NonNull ServiceConnection conn, int flags, + @NonNull UserHandle user) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Same as {@link #bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)}, but with an + * explicit non-null Handler to run the ServiceConnection callbacks on. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + @UnsupportedAppUsage(trackingBug = 136728678) + public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, + Handler handler, UserHandle user) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * For a service previously bound with {@link #bindService} or a related method, change + * how the system manages that service's process in relation to other processes. This + * doesn't modify the original bind flags that were passed in when binding, but adjusts + * how the process will be managed in some cases based on those flags. Currently only + * works on isolated processes (will be ignored for non-isolated processes). + * + *

Note that this call does not take immediate effect, but will be applied the next + * time the impacted process is adjusted for some other reason. Typically you would + * call this before then calling a new {@link #bindIsolatedService} on the service + * of interest, with that binding causing the process to be shuffled accordingly.

+ * + * @param conn The connection interface previously supplied to bindService(). This + * parameter must not be null. + * @param group A group to put this connection's process in. Upon calling here, this + * will override any previous group that was set for that process. The group + * tells the system about processes that are logically grouped together, so + * should be managed as one unit of importance (such as when being considered + * a recently used app). All processes in the same app with the same group + * are considered to be related. Supplying 0 reverts to the default behavior + * of not grouping. + * @param importance Additional importance of the processes within a group. Upon calling + * here, this will override any previous importance that was set for that + * process. The most important process is 0, and higher values are + * successively less important. You can view this as describing how + * to order the processes in an array, with the processes at the end of + * the array being the least important. This value has no meaning besides + * indicating how processes should be ordered in that array one after the + * other. This provides a way to fine-tune the system's process killing, + * guiding it to kill processes at the end of the array first. + * + * @see #bindIsolatedService + */ + public void updateServiceGroup(@NonNull ServiceConnection conn, int group, + int importance) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Disconnect from an application service. You will no longer receive + * calls as the service is restarted, and the service is now allowed to + * stop at any time. + * + * @param conn The connection interface previously supplied to + * bindService(). This parameter must not be null. + * + * @see #bindService + */ + public abstract void unbindService(@NonNull ServiceConnection conn); + + /** + * Start executing an {@link android.app.Instrumentation} class. The given + * Instrumentation component will be run by killing its target application + * (if currently running), starting the target process, instantiating the + * instrumentation component, and then letting it drive the application. + * + *

This function is not synchronous -- it returns as soon as the + * instrumentation has started and while it is running. + * + *

Instrumentation is normally only allowed to run against a package + * that is either unsigned or signed with a signature that the + * the instrumentation package is also signed with (ensuring the target + * trusts the instrumentation). + * + * @param className Name of the Instrumentation component to be run. + * @param profileFile Optional path to write profiling data as the + * instrumentation runs, or null for no profiling. + * @param arguments Additional optional arguments to pass to the + * instrumentation, or null. + * + * @return {@code true} if the instrumentation was successfully started, + * else {@code false} if it could not be found. + */ + public abstract boolean startInstrumentation(@NonNull ComponentName className, + @Nullable String profileFile, @Nullable Bundle arguments); + + /** @hide */ + @StringDef(suffix = { "_SERVICE" }, value = { + POWER_SERVICE, + + //@hide: POWER_STATS_SERVICE, + WINDOW_SERVICE, + LAYOUT_INFLATER_SERVICE, + ACCOUNT_SERVICE, + ACTIVITY_SERVICE, + ALARM_SERVICE, + NOTIFICATION_SERVICE, + ACCESSIBILITY_SERVICE, + CAPTIONING_SERVICE, + KEYGUARD_SERVICE, + LOCATION_SERVICE, + //@hide: COUNTRY_DETECTOR, + SEARCH_SERVICE, + SENSOR_SERVICE, + SENSOR_PRIVACY_SERVICE, + STORAGE_SERVICE, + STORAGE_STATS_SERVICE, + WALLPAPER_SERVICE, + TIME_ZONE_RULES_MANAGER_SERVICE, + VIBRATOR_MANAGER_SERVICE, + VIBRATOR_SERVICE, + //@hide: STATUS_BAR_SERVICE, + CONNECTIVITY_SERVICE, + PAC_PROXY_SERVICE, + VCN_MANAGEMENT_SERVICE, + //@hide: IP_MEMORY_STORE_SERVICE, + IPSEC_SERVICE, + VPN_MANAGEMENT_SERVICE, + TEST_NETWORK_SERVICE, + //@hide: UPDATE_LOCK_SERVICE, + //@hide: NETWORKMANAGEMENT_SERVICE, + NETWORK_STATS_SERVICE, + //@hide: NETWORK_POLICY_SERVICE, + WIFI_SERVICE, + WIFI_AWARE_SERVICE, + WIFI_P2P_SERVICE, + WIFI_SCANNING_SERVICE, + //@hide: LOWPAN_SERVICE, + //@hide: WIFI_RTT_SERVICE, + //@hide: ETHERNET_SERVICE, + WIFI_RTT_RANGING_SERVICE, + NSD_SERVICE, + AUDIO_SERVICE, + AUTH_SERVICE, + FINGERPRINT_SERVICE, + //@hide: FACE_SERVICE, + BIOMETRIC_SERVICE, + MEDIA_ROUTER_SERVICE, + TELEPHONY_SERVICE, + TELEPHONY_SUBSCRIPTION_SERVICE, + CARRIER_CONFIG_SERVICE, + EUICC_SERVICE, + //@hide: MMS_SERVICE, + TELECOM_SERVICE, + CLIPBOARD_SERVICE, + INPUT_METHOD_SERVICE, + TEXT_SERVICES_MANAGER_SERVICE, + TEXT_CLASSIFICATION_SERVICE, + APPWIDGET_SERVICE, + //@hide: VOICE_INTERACTION_MANAGER_SERVICE, + //@hide: BACKUP_SERVICE, + REBOOT_READINESS_SERVICE, + ROLLBACK_SERVICE, + DROPBOX_SERVICE, + //@hide: DEVICE_IDLE_CONTROLLER, + //@hide: POWER_WHITELIST_MANAGER, + DEVICE_POLICY_SERVICE, + UI_MODE_SERVICE, + DOWNLOAD_SERVICE, + NFC_SERVICE, + BLUETOOTH_SERVICE, + //@hide: SIP_SERVICE, + USB_SERVICE, + LAUNCHER_APPS_SERVICE, + //@hide: SERIAL_SERVICE, + //@hide: HDMI_CONTROL_SERVICE, + INPUT_SERVICE, + DISPLAY_SERVICE, + //@hide COLOR_DISPLAY_SERVICE, + USER_SERVICE, + RESTRICTIONS_SERVICE, + APP_OPS_SERVICE, + ROLE_SERVICE, + //@hide ROLE_CONTROLLER_SERVICE, + CAMERA_SERVICE, + //@hide: PLATFORM_COMPAT_SERVICE, + //@hide: PLATFORM_COMPAT_NATIVE_SERVICE, + PRINT_SERVICE, + CONSUMER_IR_SERVICE, + //@hide: TRUST_SERVICE, + TV_INPUT_SERVICE, + //@hide: TV_TUNER_RESOURCE_MGR_SERVICE, + //@hide: NETWORK_SCORE_SERVICE, + USAGE_STATS_SERVICE, + MEDIA_SESSION_SERVICE, + MEDIA_COMMUNICATION_SERVICE, + BATTERY_SERVICE, + JOB_SCHEDULER_SERVICE, + //@hide: PERSISTENT_DATA_BLOCK_SERVICE, + //@hide: OEM_LOCK_SERVICE, + MEDIA_PROJECTION_SERVICE, + MIDI_SERVICE, + RADIO_SERVICE, + HARDWARE_PROPERTIES_SERVICE, + //@hide: SOUND_TRIGGER_SERVICE, + SHORTCUT_SERVICE, + //@hide: CONTEXTHUB_SERVICE, + SYSTEM_HEALTH_SERVICE, + //@hide: INCIDENT_SERVICE, + //@hide: INCIDENT_COMPANION_SERVICE, + //@hide: STATS_COMPANION_SERVICE, + COMPANION_DEVICE_SERVICE, + CROSS_PROFILE_APPS_SERVICE, + //@hide: SYSTEM_UPDATE_SERVICE, + //@hide: TIME_DETECTOR_SERVICE, + //@hide: TIME_ZONE_DETECTOR_SERVICE, + PERMISSION_SERVICE, + LIGHTS_SERVICE, + //@hide: PEOPLE_SERVICE, + //@hide: DEVICE_STATE_SERVICE, + //@hide: SPEECH_RECOGNITION_SERVICE, + UWB_SERVICE, + MEDIA_METRICS_SERVICE, + MIKROM_SERVICE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ServiceName {} + + /** + * Return the handle to a system-level service by name. The class of the + * returned object varies by the requested name. Currently available names + * are: + * + *

+ *
{@link #WINDOW_SERVICE} ("window") + *
The top-level window manager in which you can place custom + * windows. The returned object is a {@link android.view.WindowManager}. Must only be obtained + * from a visual context such as Activity or a Context created with + * {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and + * visual bounds of an area on screen. + *
{@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater") + *
A {@link android.view.LayoutInflater} for inflating layout resources + * in this context. Must only be obtained from a visual context such as Activity or a Context + * created with {@link #createWindowContext(int, Bundle)}, which are adjusted to the + * configuration and visual bounds of an area on screen. + *
{@link #ACTIVITY_SERVICE} ("activity") + *
A {@link android.app.ActivityManager} for interacting with the + * global activity state of the system. + *
{@link #WALLPAPER_SERVICE} ("wallpaper") + *
A {@link android.service.wallpaper.WallpaperService} for accessing wallpapers in this + * context. Must only be obtained from a visual context such as Activity or a Context created + * with {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and + * visual bounds of an area on screen. + *
{@link #POWER_SERVICE} ("power") + *
A {@link android.os.PowerManager} for controlling power + * management. + *
{@link #ALARM_SERVICE} ("alarm") + *
A {@link android.app.AlarmManager} for receiving intents at the + * time of your choosing. + *
{@link #NOTIFICATION_SERVICE} ("notification") + *
A {@link android.app.NotificationManager} for informing the user + * of background events. + *
{@link #KEYGUARD_SERVICE} ("keyguard") + *
A {@link android.app.KeyguardManager} for controlling keyguard. + *
{@link #LOCATION_SERVICE} ("location") + *
A {@link android.location.LocationManager} for controlling location + * (e.g., GPS) updates. + *
{@link #SEARCH_SERVICE} ("search") + *
A {@link android.app.SearchManager} for handling search. + *
{@link #VIBRATOR_MANAGER_SERVICE} ("vibrator_manager") + *
A {@link android.os.VibratorManager} for accessing the device vibrators, interacting + * with individual ones and playing synchronized effects on multiple vibrators. + *
{@link #VIBRATOR_SERVICE} ("vibrator") + *
A {@link android.os.Vibrator} for interacting with the vibrator hardware. + *
{@link #CONNECTIVITY_SERVICE} ("connectivity") + *
A {@link android.net.ConnectivityManager ConnectivityManager} for + * handling management of network connections. + *
{@link #IPSEC_SERVICE} ("ipsec") + *
A {@link android.net.IpSecManager IpSecManager} for managing IPSec on + * sockets and networks. + *
{@link #WIFI_SERVICE} ("wifi") + *
A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi + * connectivity. On releases before NYC, it should only be obtained from an application + * context, and not from any other derived context to avoid memory leaks within the calling + * process. + *
{@link #WIFI_AWARE_SERVICE} ("wifiaware") + *
A {@link android.net.wifi.aware.WifiAwareManager WifiAwareManager} for management of + * Wi-Fi Aware discovery and connectivity. + *
{@link #WIFI_P2P_SERVICE} ("wifip2p") + *
A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of + * Wi-Fi Direct connectivity. + *
{@link #INPUT_METHOD_SERVICE} ("input_method") + *
An {@link android.view.inputmethod.InputMethodManager InputMethodManager} + * for management of input methods. + *
{@link #UI_MODE_SERVICE} ("uimode") + *
An {@link android.app.UiModeManager} for controlling UI modes. + *
{@link #DOWNLOAD_SERVICE} ("download") + *
A {@link android.app.DownloadManager} for requesting HTTP downloads + *
{@link #BATTERY_SERVICE} ("batterymanager") + *
A {@link android.os.BatteryManager} for managing battery state + *
{@link #JOB_SCHEDULER_SERVICE} ("taskmanager") + *
A {@link android.app.job.JobScheduler} for managing scheduled tasks + *
{@link #NETWORK_STATS_SERVICE} ("netstats") + *
A {@link android.app.usage.NetworkStatsManager NetworkStatsManager} for querying network + * usage statistics. + *
{@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties") + *
A {@link android.os.HardwarePropertiesManager} for accessing hardware properties. + *
{@link #DOMAIN_VERIFICATION_SERVICE} ("domain_verification") + *
A {@link android.content.pm.verify.domain.DomainVerificationManager} for accessing + * web domain approval state. + *
+ * + *

Note: System services obtained via this API may be closely associated with + * the Context in which they are obtained from. In general, do not share the + * service objects between various different contexts (Activities, Applications, + * Services, Providers, etc.) + * + *

Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true, + * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE}, + * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE}, + * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, + * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will + * return null. Generally, if you are running as an instant app you should always + * check whether the result of this method is {@code null}. + * + *

Note: When implementing this method, keep in mind that new services can be added on newer + * Android releases, so if you're looking for just the explicit names mentioned above, make sure + * to return {@code null} when you don't recognize the name — if you throw a + * {@link RuntimeException} exception instead, you're app might break on new Android releases. + * + * @param name The name of the desired service. + * + * @return The service or {@code null} if the name does not exist. + * + * @see #WINDOW_SERVICE + * @see android.view.WindowManager + * @see #LAYOUT_INFLATER_SERVICE + * @see android.view.LayoutInflater + * @see #ACTIVITY_SERVICE + * @see android.app.ActivityManager + * @see #POWER_SERVICE + * @see android.os.PowerManager + * @see #ALARM_SERVICE + * @see android.app.AlarmManager + * @see #NOTIFICATION_SERVICE + * @see android.app.NotificationManager + * @see #KEYGUARD_SERVICE + * @see android.app.KeyguardManager + * @see #LOCATION_SERVICE + * @see android.location.LocationManager + * @see #SEARCH_SERVICE + * @see android.app.SearchManager + * @see #SENSOR_SERVICE + * @see android.hardware.SensorManager + * @see #STORAGE_SERVICE + * @see android.os.storage.StorageManager + * @see #VIBRATOR_MANAGER_SERVICE + * @see android.os.VibratorManager + * @see #VIBRATOR_SERVICE + * @see android.os.Vibrator + * @see #CONNECTIVITY_SERVICE + * @see android.net.ConnectivityManager + * @see #WIFI_SERVICE + * @see android.net.wifi.WifiManager + * @see #AUDIO_SERVICE + * @see android.media.AudioManager + * @see #MEDIA_ROUTER_SERVICE + * @see android.media.MediaRouter + * @see #TELEPHONY_SERVICE + * @see android.telephony.TelephonyManager + * @see #TELEPHONY_SUBSCRIPTION_SERVICE + * @see android.telephony.SubscriptionManager + * @see #CARRIER_CONFIG_SERVICE + * @see android.telephony.CarrierConfigManager + * @see #EUICC_SERVICE + * @see android.telephony.euicc.EuiccManager + * @see android.telephony.MmsManager + * @see #INPUT_METHOD_SERVICE + * @see android.view.inputmethod.InputMethodManager + * @see #UI_MODE_SERVICE + * @see android.app.UiModeManager + * @see #DOWNLOAD_SERVICE + * @see android.app.DownloadManager + * @see #BATTERY_SERVICE + * @see android.os.BatteryManager + * @see #JOB_SCHEDULER_SERVICE + * @see android.app.job.JobScheduler + * @see #NETWORK_STATS_SERVICE + * @see android.app.usage.NetworkStatsManager + * @see android.os.HardwarePropertiesManager + * @see #HARDWARE_PROPERTIES_SERVICE + * @see #DOMAIN_VERIFICATION_SERVICE + * @see android.content.pm.verify.domain.DomainVerificationManager + */ + public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name); + + /** + * Return the handle to a system-level service by class. + *

+ * Currently available classes are: + * {@link android.view.WindowManager}, {@link android.view.LayoutInflater}, + * {@link android.app.ActivityManager}, {@link android.os.PowerManager}, + * {@link android.app.AlarmManager}, {@link android.app.NotificationManager}, + * {@link android.app.KeyguardManager}, {@link android.location.LocationManager}, + * {@link android.app.SearchManager}, {@link android.os.Vibrator}, + * {@link android.net.ConnectivityManager}, + * {@link android.net.wifi.WifiManager}, + * {@link android.media.AudioManager}, {@link android.media.MediaRouter}, + * {@link android.telephony.TelephonyManager}, {@link android.telephony.SubscriptionManager}, + * {@link android.view.inputmethod.InputMethodManager}, + * {@link android.app.UiModeManager}, {@link android.app.DownloadManager}, + * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler}, + * {@link android.app.usage.NetworkStatsManager}, + * {@link android.content.pm.verify.domain.DomainVerificationManager}. + *

+ * + *

+ * Note: System services obtained via this API may be closely associated with + * the Context in which they are obtained from. In general, do not share the + * service objects between various different contexts (Activities, Applications, + * Services, Providers, etc.) + *

+ * + *

Note: Instant apps, for which {@link PackageManager#isInstantApp()} returns true, + * don't have access to the following system services: {@link #DEVICE_POLICY_SERVICE}, + * {@link #FINGERPRINT_SERVICE}, {@link #KEYGUARD_SERVICE}, {@link #SHORTCUT_SERVICE}, + * {@link #USB_SERVICE}, {@link #WALLPAPER_SERVICE}, {@link #WIFI_P2P_SERVICE}, + * {@link #WIFI_SERVICE}, {@link #WIFI_AWARE_SERVICE}. For these services this method will + * return {@code null}. Generally, if you are running as an instant app you should always + * check whether the result of this method is {@code null}. + *

+ * + * @param serviceClass The class of the desired service. + * @return The service or {@code null} if the class is not a supported system service. Note: + * never throw a {@link RuntimeException} if the name is not supported. + */ + @SuppressWarnings("unchecked") + public final @Nullable T getSystemService(@NonNull Class serviceClass) { + // Because subclasses may override getSystemService(String) we cannot + // perform a lookup by class alone. We must first map the class to its + // service name then invoke the string-based method. + String serviceName = getSystemServiceName(serviceClass); + return serviceName != null ? (T)getSystemService(serviceName) : null; + } + + /** + * Gets the name of the system-level service that is represented by the specified class. + * + * @param serviceClass The class of the desired service. + * @return The service name or null if the class is not a supported system service. + */ + public abstract @Nullable String getSystemServiceName(@NonNull Class serviceClass); + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.PowerManager} for controlling power management, + * including "wake locks," which let you keep the device on while + * you're running long tasks. + */ + public static final String POWER_SERVICE = "power"; + public static final String MIKROM_SERVICE = "mikrom"; + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.PowerStatsService} for accessing power stats + * service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String POWER_STATS_SERVICE = "powerstats"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.RecoverySystem} for accessing the recovery system + * service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String RECOVERY_SERVICE = "recovery"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.SystemUpdateManager} for accessing the system update + * manager service. + * + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String SYSTEM_UPDATE_SERVICE = "system_update"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.view.WindowManager} for accessing the system's window + * manager. + * + * @see #getSystemService(String) + * @see android.view.WindowManager + */ + @UiContext + public static final String WINDOW_SERVICE = "window"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.view.LayoutInflater} for inflating layout resources in this + * context. + * + * @see #getSystemService(String) + * @see android.view.LayoutInflater + */ + @UiContext + public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.accounts.AccountManager} for receiving intents at a + * time of your choosing. + * + * @see #getSystemService(String) + * @see android.accounts.AccountManager + */ + public static final String ACCOUNT_SERVICE = "account"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.ActivityManager} for interacting with the global + * system state. + * + * @see #getSystemService(String) + * @see android.app.ActivityManager + */ + public static final String ACTIVITY_SERVICE = "activity"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.ActivityTaskManager} for interacting with the global system state. + * + * @see #getSystemService(String) + * @see android.app.ActivityTaskManager + * @hide + */ + public static final String ACTIVITY_TASK_SERVICE = "activity_task"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.UriGrantsManager} for interacting with the global system state. + * + * @see #getSystemService(String) + * @see android.app.UriGrantsManager + * @hide + */ + public static final String URI_GRANTS_SERVICE = "uri_grants"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.AlarmManager} for receiving intents at a + * time of your choosing. + * + * @see #getSystemService(String) + * @see android.app.AlarmManager + */ + public static final String ALARM_SERVICE = "alarm"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.NotificationManager} for informing the user of + * background events. + * + * @see #getSystemService(String) + * @see android.app.NotificationManager + */ + public static final String NOTIFICATION_SERVICE = "notification"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.view.accessibility.AccessibilityManager} for giving the user + * feedback for UI events through the registered event listeners. + * + * @see #getSystemService(String) + * @see android.view.accessibility.AccessibilityManager + */ + public static final String ACCESSIBILITY_SERVICE = "accessibility"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.view.accessibility.CaptioningManager} for obtaining + * captioning properties and listening for changes in captioning + * preferences. + * + * @see #getSystemService(String) + * @see android.view.accessibility.CaptioningManager + */ + public static final String CAPTIONING_SERVICE = "captioning"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.KeyguardManager} for controlling keyguard. + * + * @see #getSystemService(String) + * @see android.app.KeyguardManager + */ + public static final String KEYGUARD_SERVICE = "keyguard"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.location.LocationManager} for controlling location + * updates. + * + * @see #getSystemService(String) + * @see android.location.LocationManager + */ + public static final String LOCATION_SERVICE = "location"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.location.CountryDetector} for detecting the country that + * the user is in. + * + * @hide + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public static final String COUNTRY_DETECTOR = "country_detector"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.app.SearchManager} for handling searches. + * + *

+ * {@link Configuration#UI_MODE_TYPE_WATCH} does not support + * {@link android.app.SearchManager}. + * + * @see #getSystemService + * @see android.app.SearchManager + */ + public static final String SEARCH_SERVICE = "search"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.hardware.SensorManager} for accessing sensors. + * + * @see #getSystemService(String) + * @see android.hardware.SensorManager + */ + public static final String SENSOR_SERVICE = "sensor"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.hardware.SensorPrivacyManager} for accessing sensor privacy + * functions. + * + * @see #getSystemService(String) + * @see android.hardware.SensorPrivacyManager + * + * @hide + */ + public static final String SENSOR_PRIVACY_SERVICE = "sensor_privacy"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.os.storage.StorageManager} for accessing system storage + * functions. + * + * @see #getSystemService(String) + * @see android.os.storage.StorageManager + */ + public static final String STORAGE_SERVICE = "storage"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.app.usage.StorageStatsManager} for accessing system storage + * statistics. + * + * @see #getSystemService(String) + * @see android.app.usage.StorageStatsManager + */ + public static final String STORAGE_STATS_SERVICE = "storagestats"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * com.android.server.WallpaperService for accessing wallpapers. + * + * @see #getSystemService(String) + */ + @UiContext + public static final String WALLPAPER_SERVICE = "wallpaper"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.os.VibratorManager} + * for accessing the device vibrators, interacting with individual ones and playing synchronized + * effects on multiple vibrators. + * + * @see #getSystemService(String) + * @see android.os.VibratorManager + */ + @SuppressLint("ServiceName") + public static final String VIBRATOR_MANAGER_SERVICE = "vibrator_manager"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.os.Vibrator} for + * interacting with the vibration hardware. + * + * @deprecated Use {@link android.os.VibratorManager} to retrieve the default system vibrator. + * @see #getSystemService(String) + * @see android.os.Vibrator + */ + @Deprecated + public static final String VIBRATOR_SERVICE = "vibrator"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.app.StatusBarManager} for interacting with the status bar. + * + * @see #getSystemService(String) + * @see android.app.StatusBarManager + * + * @hide + */ + @SystemApi + @SuppressLint("ServiceName") + public static final String STATUS_BAR_SERVICE = "statusbar"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.ConnectivityManager} for handling management of + * network connections. + * + * @see #getSystemService(String) + * @see android.net.ConnectivityManager + */ + public static final String CONNECTIVITY_SERVICE = "connectivity"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.PacProxyManager} for handling management of + * pac proxy information. + * + * @see #getSystemService(String) + * @see android.net.PacProxyManager + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final String PAC_PROXY_SERVICE = "pac_proxy"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager} + * for managing Virtual Carrier Networks + * + * @see #getSystemService(String) + * @see android.net.vcn.VcnManager + * @hide + */ + public static final String VCN_MANAGEMENT_SERVICE = "vcn_management"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.net.INetd} for communicating with the network stack + * @hide + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String NETD_SERVICE = "netd"; + + /** + * Use with {@link android.os.ServiceManager.getService()} to retrieve a + * {@link INetworkStackConnector} IBinder for communicating with the network stack + * @hide + * @see NetworkStackClient + */ + public static final String NETWORK_STACK_SERVICE = "network_stack"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.TetheringManager} + * for managing tethering functions. + * @hide + * @see android.net.TetheringManager + */ + @SystemApi + public static final String TETHERING_SERVICE = "tethering"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.net.IpSecManager} for encrypting Sockets or Networks with + * IPSec. + * + * @see #getSystemService(String) + */ + public static final String IPSEC_SERVICE = "ipsec"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.VpnManager} to + * manage profiles for the platform built-in VPN. + * + * @see #getSystemService(String) + */ + public static final String VPN_MANAGEMENT_SERVICE = "vpn_management"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.ConnectivityDiagnosticsManager} for performing network connectivity diagnostics + * as well as receiving network connectivity information from the system. + * + * @see #getSystemService(String) + * @see android.net.ConnectivityDiagnosticsManager + */ + public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.TestNetworkManager} for building TUNs and limited-use Networks + * + * @see #getSystemService(String) + * @hide + */ + @TestApi @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final String TEST_NETWORK_SERVICE = "test_network"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.os.IUpdateLock} for managing runtime sequences that + * must not be interrupted by headless OTA application or similar. + * + * @hide + * @see #getSystemService(String) + * @see android.os.UpdateLock + */ + public static final String UPDATE_LOCK_SERVICE = "updatelock"; + + /** + * Constant for the internal network management service, not really a Context service. + * @hide + */ + public static final String NETWORKMANAGEMENT_SERVICE = "network_management"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link com.android.server.slice.SliceManagerService} for managing slices. + * @hide + * @see #getSystemService(String) + */ + public static final String SLICE_SERVICE = "slice"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.app.usage.NetworkStatsManager} for querying network usage stats. + * + * @see #getSystemService(String) + * @see android.app.usage.NetworkStatsManager + */ + public static final String NETWORK_STATS_SERVICE = "netstats"; + /** {@hide} */ + public static final String NETWORK_POLICY_SERVICE = "netpolicy"; + /** {@hide} */ + public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.wifi.WifiManager} for handling management of + * Wi-Fi access. + * + * @see #getSystemService(String) + * @see android.net.wifi.WifiManager + */ + public static final String WIFI_SERVICE = "wifi"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.net.wifi.wificond.WifiNl80211Manager} for handling management of the + * Wi-Fi nl802.11 daemon (wificond). + * + * @see #getSystemService(String) + * @see android.net.wifi.wificond.WifiNl80211Manager + * @hide + */ + @SystemApi + @SuppressLint("ServiceName") + public static final String WIFI_NL80211_SERVICE = "wifinl80211"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.wifi.p2p.WifiP2pManager} for handling management of + * Wi-Fi peer-to-peer connections. + * + * @see #getSystemService(String) + * @see android.net.wifi.p2p.WifiP2pManager + */ + public static final String WIFI_P2P_SERVICE = "wifip2p"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.net.wifi.aware.WifiAwareManager} for handling management of + * Wi-Fi Aware. + * + * @see #getSystemService(String) + * @see android.net.wifi.aware.WifiAwareManager + */ + public static final String WIFI_AWARE_SERVICE = "wifiaware"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.wifi.WifiScanner} for scanning the wifi universe + * + * @see #getSystemService(String) + * @see android.net.wifi.WifiScanner + * @hide + */ + @SystemApi + public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.wifi.RttManager} for ranging devices with wifi + * + * @see #getSystemService(String) + * @see android.net.wifi.RttManager + * @hide + */ + @SystemApi + @Deprecated + public static final String WIFI_RTT_SERVICE = "rttmanager"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.wifi.rtt.WifiRttManager} for ranging devices with wifi. + * + * @see #getSystemService(String) + * @see android.net.wifi.rtt.WifiRttManager + */ + public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.lowpan.LowpanManager} for handling management of + * LoWPAN access. + * + * @see #getSystemService(String) + * @see android.net.lowpan.LowpanManager + * + * @hide + */ + public static final String LOWPAN_SERVICE = "lowpan"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.EthernetManager} + * for handling management of Ethernet access. + * + * @see #getSystemService(String) + * @see android.net.EthernetManager + * + * @hide + */ + @SystemApi + public static final String ETHERNET_SERVICE = "ethernet"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.nsd.NsdManager} for handling management of network service + * discovery + * + * @see #getSystemService(String) + * @see android.net.nsd.NsdManager + */ + public static final String NSD_SERVICE = "servicediscovery"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.media.AudioManager} for handling management of volume, + * ringer modes and audio routing. + * + * @see #getSystemService(String) + * @see android.media.AudioManager + */ + public static final String AUDIO_SERVICE = "audio"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.media.MediaTranscodingManager} for transcoding media. + * + * @hide + * @see #getSystemService(String) + * @see android.media.MediaTranscodingManager + */ + @SystemApi + public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding"; + + /** + * AuthService orchestrates biometric and PIN/pattern/password authentication. + * + * BiometricService was split into two services, AuthService and BiometricService, where + * AuthService is the high level service that orchestrates all types of authentication, and + * BiometricService is a lower layer responsible only for biometric authentication. + * + * Ideally we should have renamed BiometricManager to AuthManager, because it logically + * corresponds to AuthService. However, because BiometricManager is a public API, we kept + * the old name but changed the internal implementation to use AuthService. + * + * As of now, the AUTH_SERVICE constant is only used to identify the service in + * SystemServiceRegistry and SELinux. To obtain the manager for AUTH_SERVICE, one should use + * BIOMETRIC_SERVICE with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.biometrics.BiometricManager} + * + * Map of the two services and their managers: + * [Service] [Manager] + * AuthService BiometricManager + * BiometricService N/A + * + * @hide + */ + public static final String AUTH_SERVICE = "auth"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.fingerprint.FingerprintManager} for handling management + * of fingerprints. + * + * @see #getSystemService(String) + * @see android.hardware.fingerprint.FingerprintManager + */ + public static final String FINGERPRINT_SERVICE = "fingerprint"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.face.FaceManager} for handling management + * of face authentication. + * + * @hide + * @see #getSystemService + * @see android.hardware.face.FaceManager + */ + public static final String FACE_SERVICE = "face"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.iris.IrisManager} for handling management + * of iris authentication. + * + * @hide + * @see #getSystemService + * @see android.hardware.iris.IrisManager + */ + public static final String IRIS_SERVICE = "iris"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.biometrics.BiometricManager} for handling + * biometric and PIN/pattern/password authentication. + * + * @see #getSystemService + * @see android.hardware.biometrics.BiometricManager + */ + public static final String BIOMETRIC_SERVICE = "biometric"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.media.MediaCommunicationManager} + * for managing {@link android.media.MediaSession2}. + * + * @see #getSystemService(String) + * @see android.media.MediaCommunicationManager + */ + public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication"; + + /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.media.MediaRouter} for controlling and managing + * routing of media. + * + * @see #getSystemService(String) + * @see android.media.MediaRouter + */ + public static final String MEDIA_ROUTER_SERVICE = "media_router"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.media.session.MediaSessionManager} for managing media Sessions. + * + * @see #getSystemService(String) + * @see android.media.session.MediaSessionManager + */ + public static final String MEDIA_SESSION_SERVICE = "media_session"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telephony.TelephonyManager} for handling management the + * telephony features of the device. + * + * @see #getSystemService(String) + * @see android.telephony.TelephonyManager + */ + public static final String TELEPHONY_SERVICE = "phone"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telephony.SubscriptionManager} for handling management the + * telephony subscriptions of the device. + * + * @see #getSystemService(String) + * @see android.telephony.SubscriptionManager + */ + public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telecom.TelecomManager} to manage telecom-related features + * of the device. + * + * @see #getSystemService(String) + * @see android.telecom.TelecomManager + */ + public static final String TELECOM_SERVICE = "telecom"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telephony.CarrierConfigManager} for reading carrier configuration values. + * + * @see #getSystemService(String) + * @see android.telephony.CarrierConfigManager + */ + public static final String CARRIER_CONFIG_SERVICE = "carrier_config"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telephony.euicc.EuiccManager} to manage the device eUICC (embedded SIM). + * + * @see #getSystemService(String) + * @see android.telephony.euicc.EuiccManager + */ + public static final String EUICC_SERVICE = "euicc"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telephony.euicc.EuiccCardManager} to access the device eUICC (embedded SIM). + * + * @see #getSystemService(String) + * @see android.telephony.euicc.EuiccCardManager + * @hide + */ + @SystemApi + public static final String EUICC_CARD_SERVICE = "euicc_card"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telephony.MmsManager} to send/receive MMS messages. + * + * @see #getSystemService(String) + * @see android.telephony.MmsManager + * @hide + */ + public static final String MMS_SERVICE = "mms"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.content.ClipboardManager} for accessing and modifying + * the contents of the global clipboard. + * + * @see #getSystemService(String) + * @see android.content.ClipboardManager + */ + public static final String CLIPBOARD_SERVICE = "clipboard"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link TextClassificationManager} for text classification services. + * + * @see #getSystemService(String) + * @see TextClassificationManager + */ + public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.graphics.fonts.FontManager} for font services. + * + * @see #getSystemService(String) + * @see android.graphics.fonts.FontManager + * @hide + */ + @SystemApi + @TestApi + public static final String FONT_SERVICE = "font"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link com.android.server.attention.AttentionManagerService} for attention services. + * + * @see #getSystemService(String) + * @see android.server.attention.AttentionManagerService + * @hide + */ + public static final String ATTENTION_SERVICE = "attention"; + + /** + * Official published name of the (internal) rotation resolver service. + * + * // TODO(b/178151184): change it back to rotation resolver before S release. + * + * @see #getSystemService(String) + * @hide + */ + public static final String ROTATION_RESOLVER_SERVICE = "resolver"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.view.inputmethod.InputMethodManager} for accessing input + * methods. + * + * @see #getSystemService(String) + */ + public static final String INPUT_METHOD_SERVICE = "input_method"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.view.textservice.TextServicesManager} for accessing + * text services. + * + * @see #getSystemService(String) + */ + public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.appwidget.AppWidgetManager} for accessing AppWidgets. + * + * @see #getSystemService(String) + */ + public static final String APPWIDGET_SERVICE = "appwidget"; + + /** + * Official published name of the (internal) voice interaction manager service. + * + * @hide + * @see #getSystemService(String) + */ + public static final String VOICE_INTERACTION_MANAGER_SERVICE = "voiceinteraction"; + + /** + * Official published name of the (internal) autofill service. + * + * @hide + * @see #getSystemService(String) + */ + public static final String AUTOFILL_MANAGER_SERVICE = "autofill"; + + /** + * Official published name of the (internal) text to speech manager service. + * + * @hide + * @see #getSystemService(String) + */ + public static final String TEXT_TO_SPEECH_MANAGER_SERVICE = "texttospeech"; + + /** + * Official published name of the content capture service. + * + * @hide + * @see #getSystemService(String) + */ + @TestApi + @SuppressLint("ServiceName") // TODO: This should be renamed to CONTENT_CAPTURE_SERVICE + public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; + + /** + * Official published name of the translation service. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + @SuppressLint("ServiceName") + public static final String TRANSLATION_MANAGER_SERVICE = "translation"; + + /** + * Official published name of the translation service which supports ui translation function. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + public static final String UI_TRANSLATION_SERVICE = "ui_translation"; + + /** + * Used for getting content selections and classifications for task snapshots. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; + + /** + * Official published name of the app prediction service. + * + *

NOTE: this service is optional; callers of + * {@code Context.getSystemServiceName(APP_PREDICTION_SERVICE)} should check for {@code null}. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + public static final String APP_PREDICTION_SERVICE = "app_prediction"; + + /** + * Official published name of the search ui service. + * + *

NOTE: this service is optional; callers of + * {@code Context.getSystemServiceName(SEARCH_UI_SERVICE)} should check for {@code null}. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + public static final String SEARCH_UI_SERVICE = "search_ui"; + + /** + * Used for getting the smartspace service. + * + *

NOTE: this service is optional; callers of + * {@code Context.getSystemServiceName(SMARTSPACE_SERVICE)} should check for {@code null}. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + public static final String SMARTSPACE_SERVICE = "smartspace"; + + /** + * Use with {@link #getSystemService(String)} to access the + * {@link com.android.server.voiceinteraction.SoundTriggerService}. + * + * @hide + * @see #getSystemService(String) + */ + public static final String SOUND_TRIGGER_SERVICE = "soundtrigger"; + + /** + * Use with {@link #getSystemService(String)} to access the + * {@link com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService}. + * + * @hide + * @see #getSystemService(String) + */ + public static final String SOUND_TRIGGER_MIDDLEWARE_SERVICE = "soundtrigger_middleware"; + + /** + * Used to access {@link MusicRecognitionManagerService}. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + public static final String MUSIC_RECOGNITION_SERVICE = "music_recognition"; + + /** + * Official published name of the (internal) permission service. + * + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String PERMISSION_SERVICE = "permission"; + + /** + * Official published name of the legacy (internal) permission service. + * + * @see #getSystemService(String) + * @hide + */ + //@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final String LEGACY_PERMISSION_SERVICE = "legacy_permission"; + + /** + * Official published name of the (internal) permission controller service. + * + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller"; + + /** + * Official published name of the (internal) permission checker service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String PERMISSION_CHECKER_SERVICE = "permission_checker"; + + /** + * Use with {@link #getSystemService(String) to retrieve an + * {@link android.apphibernation.AppHibernationManager}} for + * communicating with the hibernation service. + * @hide + * + * @see #getSystemService(String) + */ + @SystemApi + public static final String APP_HIBERNATION_SERVICE = "app_hibernation"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.app.backup.IBackupManager IBackupManager} for communicating + * with the backup mechanism. + * @hide + * + * @see #getSystemService(String) + */ + @SystemApi + public static final String BACKUP_SERVICE = "backup"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.content.rollback.RollbackManager} for communicating + * with the rollback manager + * + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String ROLLBACK_SERVICE = "rollback"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.scheduling.RebootReadinessManager} for communicating + * with the reboot readiness detector. + * + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String REBOOT_READINESS_SERVICE = "reboot_readiness"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.DropBoxManager} instance for recording + * diagnostic logs. + * @see #getSystemService(String) + */ + public static final String DROPBOX_SERVICE = "dropbox"; + + /** + * System service name for the DeviceIdleManager. + * @see #getSystemService(String) + * @hide + */ + @TestApi + @SuppressLint("ServiceName") // TODO: This should be renamed to DEVICE_IDLE_SERVICE + public static final String DEVICE_IDLE_CONTROLLER = "deviceidle"; + + /** + * System service name for the PowerWhitelistManager. + * + * @see #getSystemService(String) + * @hide + */ + @TestApi + @Deprecated + @SuppressLint("ServiceName") + public static final String POWER_WHITELIST_MANAGER = "power_whitelist"; + + /** + * System service name for the PowerExemptionManager. + * + * @see #getSystemService(String) + * @hide + */ + @TestApi + public static final String POWER_EXEMPTION_SERVICE = "power_exemption"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.admin.DevicePolicyManager} for working with global + * device policy management. + * + * @see #getSystemService(String) + */ + public static final String DEVICE_POLICY_SERVICE = "device_policy"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.UiModeManager} for controlling UI modes. + * + * @see #getSystemService(String) + */ + public static final String UI_MODE_SERVICE = "uimode"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.DownloadManager} for requesting HTTP downloads. + * + * @see #getSystemService(String) + */ + public static final String DOWNLOAD_SERVICE = "download"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.BatteryManager} for managing battery state. + * + * @see #getSystemService(String) + */ + public static final String BATTERY_SERVICE = "batterymanager"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.nfc.NfcManager} for using NFC. + * + * @see #getSystemService(String) + */ + public static final String NFC_SERVICE = "nfc"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.bluetooth.BluetoothManager} for using Bluetooth. + * + * @see #getSystemService(String) + */ + public static final String BLUETOOTH_SERVICE = "bluetooth"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.net.sip.SipManager} for accessing the SIP related service. + * + * @see #getSystemService(String) + */ + /** @hide */ + public static final String SIP_SERVICE = "sip"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.hardware.usb.UsbManager} for access to USB devices (as a USB host) + * and for controlling this device's behavior as a USB device. + * + * @see #getSystemService(String) + * @see android.hardware.usb.UsbManager + */ + public static final String USB_SERVICE = "usb"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * Use with {@link #getSystemService} to retrieve a {@link + * android.debug.AdbManager} for access to ADB debug functions. + * + * @see #getSystemService(String) + * @see android.debug.AdbManager + * + * @hide + */ + public static final String ADB_SERVICE = "adb"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.hardware.SerialManager} for access to serial ports. + * + * @see #getSystemService(String) + * @see android.hardware.SerialManager + * + * @hide + */ + public static final String SERIAL_SERVICE = "serial"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.hdmi.HdmiControlManager} for controlling and managing + * HDMI-CEC protocol. + * + * @see #getSystemService(String) + * @see android.hardware.hdmi.HdmiControlManager + * @hide + */ + @SystemApi + public static final String HDMI_CONTROL_SERVICE = "hdmi_control"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.input.InputManager} for interacting with input devices. + * + * @see #getSystemService(String) + * @see android.hardware.input.InputManager + */ + public static final String INPUT_SERVICE = "input"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.display.DisplayManager} for interacting with display devices. + * + * @see #getSystemService(String) + * @see android.hardware.display.DisplayManager + */ + public static final String DISPLAY_SERVICE = "display"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.display.ColorDisplayManager} for controlling color transforms. + * + * @see #getSystemService(String) + * @see android.hardware.display.ColorDisplayManager + * @hide + */ + public static final String COLOR_DISPLAY_SERVICE = "color_display"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.UserManager} for managing users on devices that support multiple users. + * + * @see #getSystemService(String) + * @see android.os.UserManager + */ + public static final String USER_SERVICE = "user"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.content.pm.LauncherApps} for querying and monitoring launchable apps across + * profiles of a user. + * + * @see #getSystemService(String) + * @see android.content.pm.LauncherApps + */ + public static final String LAUNCHER_APPS_SERVICE = "launcherapps"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.content.RestrictionsManager} for retrieving application restrictions + * and requesting permissions for restricted operations. + * @see #getSystemService(String) + * @see android.content.RestrictionsManager + */ + public static final String RESTRICTIONS_SERVICE = "restrictions"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.AppOpsManager} for tracking application operations + * on the device. + * + * @see #getSystemService(String) + * @see android.app.AppOpsManager + */ + public static final String APP_OPS_SERVICE = "appops"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.app.role.RoleManager} + * for managing roles. + * + * @see #getSystemService(String) + * @see android.app.role.RoleManager + */ + public static final String ROLE_SERVICE = "role"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.camera2.CameraManager} for interacting with + * camera devices. + * + * @see #getSystemService(String) + * @see android.hardware.camera2.CameraManager + */ + public static final String CAMERA_SERVICE = "camera"; + + /** + * {@link android.print.PrintManager} for printing and managing + * printers and print tasks. + * + * @see #getSystemService(String) + * @see android.print.PrintManager + */ + public static final String PRINT_SERVICE = "print"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.companion.CompanionDeviceManager} for managing companion devices + * + * @see #getSystemService(String) + * @see android.companion.CompanionDeviceManager + */ + public static final String COMPANION_DEVICE_SERVICE = "companiondevice"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.ConsumerIrManager} for transmitting infrared + * signals from the device. + * + * @see #getSystemService(String) + * @see android.hardware.ConsumerIrManager + */ + public static final String CONSUMER_IR_SERVICE = "consumer_ir"; + + /** + * {@link android.app.trust.TrustManager} for managing trust agents. + * @see #getSystemService(String) + * @see android.app.trust.TrustManager + * @hide + */ + public static final String TRUST_SERVICE = "trust"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.media.tv.TvInputManager} for interacting with TV inputs + * on the device. + * + * @see #getSystemService(String) + * @see android.media.tv.TvInputManager + */ + public static final String TV_INPUT_SERVICE = "tv_input"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.media.tv.TunerResourceManager} for interacting with TV + * tuner resources on the device. + * + * @see #getSystemService(String) + * @see android.media.tv.TunerResourceManager + * @hide + */ + public static final String TV_TUNER_RESOURCE_MGR_SERVICE = "tv_tuner_resource_mgr"; + + /** + * {@link android.net.NetworkScoreManager} for managing network scoring. + * @see #getSystemService(String) + * @see android.net.NetworkScoreManager + * @hide + */ + @SystemApi + public static final String NETWORK_SCORE_SERVICE = "network_score"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.app.usage.UsageStatsManager} for querying device usage stats. + * + * @see #getSystemService(String) + * @see android.app.usage.UsageStatsManager + */ + public static final String USAGE_STATS_SERVICE = "usagestats"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.app.job.JobScheduler} instance for managing occasional + * background tasks. + * @see #getSystemService(String) + * @see android.app.job.JobScheduler + */ + public static final String JOB_SCHEDULER_SERVICE = "jobscheduler"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.service.persistentdata.PersistentDataBlockManager} instance + * for interacting with a storage device that lives across factory resets. + * + * @see #getSystemService(String) + * @see android.service.persistentdata.PersistentDataBlockManager + * @hide + */ + @SystemApi + public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.service.oemlock.OemLockManager} instance for managing the OEM lock. + * + * @see #getSystemService(String) + * @see android.service.oemlock.OemLockManager + * @hide + */ + @SystemApi + public static final String OEM_LOCK_SERVICE = "oem_lock"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.media.projection.MediaProjectionManager} instance for managing + * media projection sessions. + * @see #getSystemService(String) + * @see android.media.projection.MediaProjectionManager + */ + public static final String MEDIA_PROJECTION_SERVICE = "media_projection"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.media.midi.MidiManager} for accessing the MIDI service. + * + * @see #getSystemService(String) + */ + public static final String MIDI_SERVICE = "midi"; + + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.radio.RadioManager} for accessing the broadcast radio service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String RADIO_SERVICE = "broadcastradio"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.HardwarePropertiesManager} for accessing the hardware properties service. + * + * @see #getSystemService(String) + */ + public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.ThermalService} for accessing the thermal service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String THERMAL_SERVICE = "thermalservice"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.PerformanceHintManager} for accessing the performance hinting service. + * + * @see #getSystemService(String) + */ + public static final String PERFORMANCE_HINT_SERVICE = "performance_hint"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.content.pm.ShortcutManager} for accessing the launcher shortcut service. + * + * @see #getSystemService(String) + * @see android.content.pm.ShortcutManager + */ + public static final String SHORTCUT_SERVICE = "shortcut"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.hardware.location.ContextHubManager} for accessing context hubs. + * + * @see #getSystemService(String) + * @see android.hardware.location.ContextHubManager + * + * @hide + */ + @SystemApi + public static final String CONTEXTHUB_SERVICE = "contexthub"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.os.health.SystemHealthManager} for accessing system health (battery, power, + * memory, etc) metrics. + * + * @see #getSystemService(String) + */ + public static final String SYSTEM_HEALTH_SERVICE = "systemhealth"; + + /** + * Gatekeeper Service. + * @hide + */ + public static final String GATEKEEPER_SERVICE = "android.service.gatekeeper.IGateKeeperService"; + + /** + * Service defining the policy for access to device identifiers. + * @hide + */ + public static final String DEVICE_IDENTIFIERS_SERVICE = "device_identifiers"; + + /** + * Service to report a system health "incident" + * @hide + */ + public static final String INCIDENT_SERVICE = "incident"; + + /** + * Service to assist incidentd and dumpstated in reporting status to the user + * and in confirming authorization to take an incident report or bugreport + * @hide + */ + public static final String INCIDENT_COMPANION_SERVICE = "incidentcompanion"; + + /** + * Service to assist {@link android.app.StatsManager} that lives in system server. + * @hide + */ + public static final String STATS_MANAGER_SERVICE = "statsmanager"; + + /** + * Service to assist statsd in obtaining general stats. + * @hide + */ + public static final String STATS_COMPANION_SERVICE = "statscompanion"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an {@link android.app.StatsManager}. + * @hide + */ + @SystemApi + public static final String STATS_MANAGER = "stats"; + + /** + * Use with {@link android.os.ServiceManager.getService()} to retrieve a + * {@link IPlatformCompat} IBinder for communicating with the platform compat service. + * @hide + */ + public static final String PLATFORM_COMPAT_SERVICE = "platform_compat"; + + /** + * Use with {@link android.os.ServiceManager.getService()} to retrieve a + * {@link IPlatformCompatNative} IBinder for native code communicating with the platform compat + * service. + * @hide + */ + public static final String PLATFORM_COMPAT_NATIVE_SERVICE = "platform_compat_native"; + + /** + * Service to capture a bugreport. + * @see #getSystemService(String) + * @see android.os.BugreportManager + */ + public static final String BUGREPORT_SERVICE = "bugreport"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.content.om.OverlayManager} for managing overlay packages. + * + * @see #getSystemService(String) + * @see android.content.om.OverlayManager + * @hide + */ + public static final String OVERLAY_SERVICE = "overlay"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {android.os.IIdmap2} for managing idmap files (used by overlay + * packages). + * + * @see #getSystemService(String) + * @hide + */ + public static final String IDMAP_SERVICE = "idmap"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link VrManager} for accessing the VR service. + * + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String VR_SERVICE = "vrmanager"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.app.timezone.ITimeZoneRulesManager}. + * @hide + * + * @see #getSystemService(String) + */ + public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.content.pm.CrossProfileApps} for cross profile operations. + * + * @see #getSystemService(String) + */ + public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps"; + + /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.se.omapi.ISecureElementService} + * for accessing the SecureElementService. + * + * @hide + */ + @SystemApi + public static final String SECURE_ELEMENT_SERVICE = "secure_element"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.app.timedetector.TimeDetector}. + * @hide + * + * @see #getSystemService(String) + */ + public static final String TIME_DETECTOR_SERVICE = "time_detector"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.app.timezonedetector.TimeZoneDetector}. + * @hide + * + * @see #getSystemService(String) + */ + public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an {@link TimeManager}. + * @hide + * + * @see #getSystemService(String) + */ + public static final String TIME_MANAGER = "time_manager"; + + /** + * Binder service name for {@link AppBindingService}. + * @hide + */ + public static final String APP_BINDING_SERVICE = "app_binding"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.telephony.ims.ImsManager}. + */ + public static final String TELEPHONY_IMS_SERVICE = "telephony_ims"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.os.SystemConfigManager}. + * @hide + */ + @SystemApi + public static final String SYSTEM_CONFIG_SERVICE = "system_config"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.telephony.ims.RcsMessageManager}. + * @hide + */ + public static final String TELEPHONY_RCS_MESSAGE_SERVICE = "ircsmessage"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.os.image.DynamicSystemManager}. + * @hide + */ + public static final String DYNAMIC_SYSTEM_SERVICE = "dynamic_system"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.app.blob.BlobStoreManager} for contributing and accessing data blobs + * from the blob store maintained by the system. + * + * @see #getSystemService(String) + * @see android.app.blob.BlobStoreManager + */ + public static final String BLOB_STORE_SERVICE = "blob_store"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link TelephonyRegistryManager}. + * @hide + */ + public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.os.BatteryStatsManager}. + * @hide + */ + @SystemApi + @SuppressLint("ServiceName") + public static final String BATTERY_STATS_SERVICE = "batterystats"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.app.appsearch.AppSearchManager} for + * indexing and querying app data managed by the system. + * + * @see #getSystemService(String) + */ + public static final String APP_SEARCH_SERVICE = "app_search"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.content.integrity.AppIntegrityManager}. + * @hide + */ + @SystemApi + public static final String APP_INTEGRITY_SERVICE = "app_integrity"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.content.pm.DataLoaderManager}. + * @hide + */ + public static final String DATA_LOADER_MANAGER_SERVICE = "dataloader_manager"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.os.incremental.IncrementalManager}. + * @hide + */ + public static final String INCREMENTAL_SERVICE = "incremental"; + + /** + * Use with {@link #getSystemService(String)} to retrieve an + * {@link android.security.FileIntegrityManager}. + * @see #getSystemService(String) + * @see android.security.FileIntegrityManager + */ + public static final String FILE_INTEGRITY_SERVICE = "file_integrity"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.lights.LightsManager} for controlling device lights. + * + * @see #getSystemService(String) + * @hide + */ + public static final String LIGHTS_SERVICE = "lights"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.uwb.UwbManager}. + * + * @see #getSystemService(String) + * @hide + */ + public static final String UWB_SERVICE = "uwb"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.DreamManager} for controlling Dream states. + * + * @see #getSystemService(String) + + * @hide + */ + @TestApi + public static final String DREAM_SERVICE = "dream"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.telephony.SmsManager} for accessing Sms functionality. + * + * @see #getSystemService(String) + + * @hide + */ + public static final String SMS_SERVICE = "sms"; + + /** + * Use with {@link #getSystemService(String)} to access a {@link PeopleManager} to interact + * with your published conversations. + * + * @see #getSystemService(String) + */ + public static final String PEOPLE_SERVICE = "people"; + + /** + * Use with {@link #getSystemService(String)} to access device state service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String DEVICE_STATE_SERVICE = "device_state"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.media.metrics.MediaMetricsManager} for interacting with media metrics + * on the device. + * + * @see #getSystemService(String) + * @see android.media.metrics.MediaMetricsManager + */ + public static final String MEDIA_METRICS_SERVICE = "media_metrics"; + + /** + * Use with {@link #getSystemService(String)} to access system speech recognition service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link GameManager}. + * + * @see #getSystemService(String) + */ + public static final String GAME_SERVICE = "game"; + + /** + * Use with {@link #getSystemService(String)} to access + * {@link android.content.pm.verify.domain.DomainVerificationManager} to retrieve approval and + * user state for declared web domains. + * + * @see #getSystemService(String) + * @see android.content.pm.verify.domain.DomainVerificationManager + */ + public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification"; + + /** + * Use with {@link #getSystemService(String)} to access + * {@link android.view.displayhash.DisplayHashManager} to handle display hashes. + * + * @see #getSystemService(String) + */ + public static final String DISPLAY_HASH_SERVICE = "display_hash"; + + /** + * Determine whether the given permission is allowed for a particular + * process and user ID running in the system. + * + * @param permission The name of the permission being checked. + * @param pid The process ID being checked against. Must be > 0. + * @param uid The user ID being checked against. A uid of 0 is the root + * user, which will pass every permission check. + * + * @return {@link PackageManager#PERMISSION_GRANTED} if the given + * pid/uid is allowed that permission, or + * {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see PackageManager#checkPermission(String, String) + * @see #checkCallingPermission + */ + @CheckResult(suggest="#enforcePermission(String,int,int,String)") + @PackageManager.PermissionResult + public abstract int checkPermission(@NonNull String permission, int pid, int uid); + + /** @hide */ + @SuppressWarnings("HiddenAbstractMethod") + @PackageManager.PermissionResult + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public abstract int checkPermission(@NonNull String permission, int pid, int uid, + IBinder callerToken); + + /** + * Determine whether the calling process of an IPC you are handling has been + * granted a particular permission. This is basically the same as calling + * {@link #checkPermission(String, int, int)} with the pid and uid returned + * by {@link android.os.Binder#getCallingPid} and + * {@link android.os.Binder#getCallingUid}. One important difference + * is that if you are not currently processing an IPC, this function + * will always fail. This is done to protect against accidentally + * leaking permissions; you can use {@link #checkCallingOrSelfPermission} + * to avoid this protection. + * + * @param permission The name of the permission being checked. + * + * @return {@link PackageManager#PERMISSION_GRANTED} if the calling + * pid/uid is allowed that permission, or + * {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see PackageManager#checkPermission(String, String) + * @see #checkPermission + * @see #checkCallingOrSelfPermission + */ + @CheckResult(suggest="#enforceCallingPermission(String,String)") + @PackageManager.PermissionResult + public abstract int checkCallingPermission(@NonNull String permission); + + /** + * Determine whether the calling process of an IPC or you have been + * granted a particular permission. This is the same as + * {@link #checkCallingPermission}, except it grants your own permissions + * if you are not currently processing an IPC. Use with care! + * + * @param permission The name of the permission being checked. + * + * @return {@link PackageManager#PERMISSION_GRANTED} if the calling + * pid/uid is allowed that permission, or + * {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see PackageManager#checkPermission(String, String) + * @see #checkPermission + * @see #checkCallingPermission + */ + @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") + @PackageManager.PermissionResult + public abstract int checkCallingOrSelfPermission(@NonNull String permission); + + /** + * Determine whether you have been granted a particular permission. + * + * @param permission The name of the permission being checked. + * + * @return {@link PackageManager#PERMISSION_GRANTED} if you have the + * permission, or {@link PackageManager#PERMISSION_DENIED} if not. + * + * @see PackageManager#checkPermission(String, String) + * @see #checkCallingPermission(String) + */ + @PackageManager.PermissionResult + public abstract int checkSelfPermission(@NonNull String permission); + + /** + * If the given permission is not allowed for a particular process + * and user ID running in the system, throw a {@link SecurityException}. + * + * @param permission The name of the permission being checked. + * @param pid The process ID being checked against. Must be > 0. + * @param uid The user ID being checked against. A uid of 0 is the root + * user, which will pass every permission check. + * @param message A message to include in the exception if it is thrown. + * + * @see #checkPermission(String, int, int) + */ + public abstract void enforcePermission( + @NonNull String permission, int pid, int uid, @Nullable String message); + + /** + * If the calling process of an IPC you are handling has not been + * granted a particular permission, throw a {@link + * SecurityException}. This is basically the same as calling + * {@link #enforcePermission(String, int, int, String)} with the + * pid and uid returned by {@link android.os.Binder#getCallingPid} + * and {@link android.os.Binder#getCallingUid}. One important + * difference is that if you are not currently processing an IPC, + * this function will always throw the SecurityException. This is + * done to protect against accidentally leaking permissions; you + * can use {@link #enforceCallingOrSelfPermission} to avoid this + * protection. + * + * @param permission The name of the permission being checked. + * @param message A message to include in the exception if it is thrown. + * + * @see #checkCallingPermission(String) + */ + public abstract void enforceCallingPermission( + @NonNull String permission, @Nullable String message); + + /** + * If neither you nor the calling process of an IPC you are + * handling has been granted a particular permission, throw a + * {@link SecurityException}. This is the same as {@link + * #enforceCallingPermission}, except it grants your own + * permissions if you are not currently processing an IPC. Use + * with care! + * + * @param permission The name of the permission being checked. + * @param message A message to include in the exception if it is thrown. + * + * @see #checkCallingOrSelfPermission(String) + */ + public abstract void enforceCallingOrSelfPermission( + @NonNull String permission, @Nullable String message); + + /** + * Grant permission to access a specific Uri to another package, regardless + * of whether that package has general permission to access the Uri's + * content provider. This can be used to grant specific, temporary + * permissions, typically in response to user interaction (such as the + * user opening an attachment that you would like someone else to + * display). + * + *

Normally you should use {@link Intent#FLAG_GRANT_READ_URI_PERMISSION + * Intent.FLAG_GRANT_READ_URI_PERMISSION} or + * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION + * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} with the Intent being used to + * start an activity instead of this function directly. If you use this + * function directly, you should be sure to call + * {@link #revokeUriPermission} when the target should no longer be allowed + * to access it. + * + *

To succeed, the content provider owning the Uri must have set the + * {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions + * grantUriPermissions} attribute in its manifest or included the + * {@link android.R.styleable#AndroidManifestGrantUriPermission + * <grant-uri-permissions>} tag. + * + * @param toPackage The package you would like to allow to access the Uri. + * @param uri The Uri you would like to grant access to. + * @param modeFlags The desired access modes. + * + * @see #revokeUriPermission + */ + public abstract void grantUriPermission(String toPackage, Uri uri, + @Intent.GrantUriMode int modeFlags); + + /** + * Remove all permissions to access a particular content provider Uri + * that were previously added with {@link #grantUriPermission} or any other mechanism. + * The given Uri will match all previously granted Uris that are the same or a + * sub-path of the given Uri. That is, revoking "content://foo/target" will + * revoke both "content://foo/target" and "content://foo/target/sub", but not + * "content://foo". It will not remove any prefix grants that exist at a + * higher level. + * + *

Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, if you did not have + * regular permission access to a Uri, but had received access to it through + * a specific Uri permission grant, you could not revoke that grant with this + * function and a {@link SecurityException} would be thrown. As of + * {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this function will not throw a security + * exception, but will remove whatever permission grants to the Uri had been given to the app + * (or none).

+ * + *

Unlike {@link #revokeUriPermission(String, Uri, int)}, this method impacts all permission + * grants matching the given Uri, for any package they had been granted to, through any + * mechanism this had happened (such as indirectly through the clipboard, activity launch, + * service start, etc). That means this can be potentially dangerous to use, as it can + * revoke grants that another app could be strongly expecting to stick around.

+ * + * @param uri The Uri you would like to revoke access to. + * @param modeFlags The access modes to revoke. + * + * @see #grantUriPermission + */ + public abstract void revokeUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags); + + /** + * Remove permissions to access a particular content provider Uri + * that were previously added with {@link #grantUriPermission} for a specific target + * package. The given Uri will match all previously granted Uris that are the same or a + * sub-path of the given Uri. That is, revoking "content://foo/target" will + * revoke both "content://foo/target" and "content://foo/target/sub", but not + * "content://foo". It will not remove any prefix grants that exist at a + * higher level. + * + *

Unlike {@link #revokeUriPermission(Uri, int)}, this method will only + * revoke permissions that had been explicitly granted through {@link #grantUriPermission} + * and only for the package specified. Any matching grants that have happened through + * other mechanisms (clipboard, activity launching, service starting, etc) will not be + * removed.

+ * + * @param toPackage The package you had previously granted access to. + * @param uri The Uri you would like to revoke access to. + * @param modeFlags The access modes to revoke. + * + * @see #grantUriPermission + */ + public abstract void revokeUriPermission(String toPackage, Uri uri, + @Intent.AccessUriMode int modeFlags); + + /** + * Determine whether a particular process and user ID has been granted + * permission to access a specific URI. This only checks for permissions + * that have been explicitly granted -- if the given process/uid has + * more general access to the URI's content provider then this check will + * always fail. + * + * @param uri The uri that is being checked. + * @param pid The process ID being checked against. Must be > 0. + * @param uid The user ID being checked against. A uid of 0 is the root + * user, which will pass every permission check. + * @param modeFlags The access modes to check. + * + * @return {@link PackageManager#PERMISSION_GRANTED} if the given + * pid/uid is allowed to access that uri, or + * {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see #checkCallingUriPermission + */ + @CheckResult(suggest="#enforceUriPermission(Uri,int,int,String)") + @PackageManager.PermissionResult + public abstract int checkUriPermission(Uri uri, int pid, int uid, + @Intent.AccessUriMode int modeFlags); + + /** + * Determine whether a particular process and user ID has been granted + * permission to access a list of URIs. This only checks for permissions + * that have been explicitly granted -- if the given process/uid has + * more general access to the URI's content provider then this check will + * always fail. + * + * @param uris The list of URIs that is being checked. + * @param pid The process ID being checked against. Must be > 0. + * @param uid The user ID being checked against. A uid of 0 is the root + * user, which will pass every permission check. + * @param modeFlags The access modes to check for the list of uris + * + * @return Array of permission grants corresponding to each entry in the list of uris. + * {@link PackageManager#PERMISSION_GRANTED} if the given pid/uid is allowed to access that uri, + * or {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see #checkCallingUriPermission + */ + @NonNull + @PackageManager.PermissionResult + public int[] checkUriPermissions(@NonNull List uris, int pid, int uid, + @Intent.AccessUriMode int modeFlags) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** @hide */ + @SuppressWarnings("HiddenAbstractMethod") + @PackageManager.PermissionResult + public abstract int checkUriPermission(Uri uri, int pid, int uid, + @Intent.AccessUriMode int modeFlags, IBinder callerToken); + + /** + * Determine whether the calling process and user ID has been + * granted permission to access a specific URI. This is basically + * the same as calling {@link #checkUriPermission(Uri, int, int, + * int)} with the pid and uid returned by {@link + * android.os.Binder#getCallingPid} and {@link + * android.os.Binder#getCallingUid}. One important difference is + * that if you are not currently processing an IPC, this function + * will always fail. + * + * @param uri The uri that is being checked. + * @param modeFlags The access modes to check. + * + * @return {@link PackageManager#PERMISSION_GRANTED} if the caller + * is allowed to access that uri, or + * {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see #checkUriPermission(Uri, int, int, int) + */ + @CheckResult(suggest="#enforceCallingUriPermission(Uri,int,String)") + @PackageManager.PermissionResult + public abstract int checkCallingUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags); + + /** + * Determine whether the calling process and user ID has been + * granted permission to access a list of URIs. This is basically + * the same as calling {@link #checkUriPermissions(List, int, int, int)} + * with the pid and uid returned by {@link + * android.os.Binder#getCallingPid} and {@link + * android.os.Binder#getCallingUid}. One important difference is + * that if you are not currently processing an IPC, this function + * will always fail. + * + * @param uris The list of URIs that is being checked. + * @param modeFlags The access modes to check. + * + * @return Array of permission grants corresponding to each entry in the list of uris. + * {@link PackageManager#PERMISSION_GRANTED} if the given pid/uid is allowed to access that uri, + * or {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see #checkUriPermission(Uri, int, int, int) + */ + @NonNull + @PackageManager.PermissionResult + public int[] checkCallingUriPermissions(@NonNull List uris, + @Intent.AccessUriMode int modeFlags) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Determine whether the calling process of an IPC or you has been granted + * permission to access a specific URI. This is the same as + * {@link #checkCallingUriPermission}, except it grants your own permissions + * if you are not currently processing an IPC. Use with care! + * + * @param uri The uri that is being checked. + * @param modeFlags The access modes to check. + * + * @return {@link PackageManager#PERMISSION_GRANTED} if the caller + * is allowed to access that uri, or + * {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see #checkCallingUriPermission + */ + @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") + @PackageManager.PermissionResult + public abstract int checkCallingOrSelfUriPermission(Uri uri, + @Intent.AccessUriMode int modeFlags); + + /** + * Determine whether the calling process of an IPC or you has been granted + * permission to access a list of URIs. This is the same as + * {@link #checkCallingUriPermission}, except it grants your own permissions + * if you are not currently processing an IPC. Use with care! + * + * @param uris The list of URIs that is being checked. + * @param modeFlags The access modes to check. + * + * @return Array of permission grants corresponding to each entry in the list of uris. + * {@link PackageManager#PERMISSION_GRANTED} if the given pid/uid is allowed to access that uri, + * or {@link PackageManager#PERMISSION_DENIED} if it is not. + * + * @see #checkCallingUriPermission + */ + @NonNull + @PackageManager.PermissionResult + public int[] checkCallingOrSelfUriPermissions(@NonNull List uris, + @Intent.AccessUriMode int modeFlags) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Check both a Uri and normal permission. This allows you to perform + * both {@link #checkPermission} and {@link #checkUriPermission} in one + * call. + * + * @param uri The Uri whose permission is to be checked, or null to not + * do this check. + * @param readPermission The permission that provides overall read access, + * or null to not do this check. + * @param writePermission The permission that provides overall write + * access, or null to not do this check. + * @param pid The process ID being checked against. Must be > 0. + * @param uid The user ID being checked against. A uid of 0 is the root + * user, which will pass every permission check. + * @param modeFlags The access modes to check. + * + * @return {@link PackageManager#PERMISSION_GRANTED} if the caller + * is allowed to access that uri or holds one of the given permissions, or + * {@link PackageManager#PERMISSION_DENIED} if it is not. + */ + @CheckResult(suggest="#enforceUriPermission(Uri,String,String,int,int,int,String)") + @PackageManager.PermissionResult + public abstract int checkUriPermission(@Nullable Uri uri, @Nullable String readPermission, + @Nullable String writePermission, int pid, int uid, + @Intent.AccessUriMode int modeFlags); + + /** + * If a particular process and user ID has not been granted + * permission to access a specific URI, throw {@link + * SecurityException}. This only checks for permissions that have + * been explicitly granted -- if the given process/uid has more + * general access to the URI's content provider then this check + * will always fail. + * + * @param uri The uri that is being checked. + * @param pid The process ID being checked against. Must be > 0. + * @param uid The user ID being checked against. A uid of 0 is the root + * user, which will pass every permission check. + * @param modeFlags The access modes to enforce. + * @param message A message to include in the exception if it is thrown. + * + * @see #checkUriPermission(Uri, int, int, int) + */ + public abstract void enforceUriPermission( + Uri uri, int pid, int uid, @Intent.AccessUriMode int modeFlags, String message); + + /** + * If the calling process and user ID has not been granted + * permission to access a specific URI, throw {@link + * SecurityException}. This is basically the same as calling + * {@link #enforceUriPermission(Uri, int, int, int, String)} with + * the pid and uid returned by {@link + * android.os.Binder#getCallingPid} and {@link + * android.os.Binder#getCallingUid}. One important difference is + * that if you are not currently processing an IPC, this function + * will always throw a SecurityException. + * + * @param uri The uri that is being checked. + * @param modeFlags The access modes to enforce. + * @param message A message to include in the exception if it is thrown. + * + * @see #checkCallingUriPermission(Uri, int) + */ + public abstract void enforceCallingUriPermission( + Uri uri, @Intent.AccessUriMode int modeFlags, String message); + + /** + * If the calling process of an IPC or you has not been + * granted permission to access a specific URI, throw {@link + * SecurityException}. This is the same as {@link + * #enforceCallingUriPermission}, except it grants your own + * permissions if you are not currently processing an IPC. Use + * with care! + * + * @param uri The uri that is being checked. + * @param modeFlags The access modes to enforce. + * @param message A message to include in the exception if it is thrown. + * + * @see #checkCallingOrSelfUriPermission(Uri, int) + */ + public abstract void enforceCallingOrSelfUriPermission( + Uri uri, @Intent.AccessUriMode int modeFlags, String message); + + /** + * Enforce both a Uri and normal permission. This allows you to perform + * both {@link #enforcePermission} and {@link #enforceUriPermission} in one + * call. + * + * @param uri The Uri whose permission is to be checked, or null to not + * do this check. + * @param readPermission The permission that provides overall read access, + * or null to not do this check. + * @param writePermission The permission that provides overall write + * access, or null to not do this check. + * @param pid The process ID being checked against. Must be > 0. + * @param uid The user ID being checked against. A uid of 0 is the root + * user, which will pass every permission check. + * @param modeFlags The access modes to enforce. + * @param message A message to include in the exception if it is thrown. + * + * @see #checkUriPermission(Uri, String, String, int, int, int) + */ + public abstract void enforceUriPermission( + @Nullable Uri uri, @Nullable String readPermission, + @Nullable String writePermission, int pid, int uid, @Intent.AccessUriMode int modeFlags, + @Nullable String message); + + /** @hide */ + @IntDef(flag = true, prefix = { "CONTEXT_" }, value = { + CONTEXT_INCLUDE_CODE, + CONTEXT_IGNORE_SECURITY, + CONTEXT_RESTRICTED, + CONTEXT_DEVICE_PROTECTED_STORAGE, + CONTEXT_CREDENTIAL_PROTECTED_STORAGE, + CONTEXT_REGISTER_PACKAGE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CreatePackageOptions {} + + /** + * Flag for use with {@link #createPackageContext}: include the application + * code with the context. This means loading code into the caller's + * process, so that {@link #getClassLoader()} can be used to instantiate + * the application's classes. Setting this flags imposes security + * restrictions on what application context you can access; if the + * requested application can not be safely loaded into your process, + * java.lang.SecurityException will be thrown. If this flag is not set, + * there will be no restrictions on the packages that can be loaded, + * but {@link #getClassLoader} will always return the default system + * class loader. + */ + public static final int CONTEXT_INCLUDE_CODE = 0x00000001; + + /** + * Flag for use with {@link #createPackageContext}: ignore any security + * restrictions on the Context being requested, allowing it to always + * be loaded. For use with {@link #CONTEXT_INCLUDE_CODE} to allow code + * to be loaded into a process even when it isn't safe to do so. Use + * with extreme care! + */ + public static final int CONTEXT_IGNORE_SECURITY = 0x00000002; + + /** + * Flag for use with {@link #createPackageContext}: a restricted context may + * disable specific features. For instance, a View associated with a restricted + * context would ignore particular XML attributes. + */ + public static final int CONTEXT_RESTRICTED = 0x00000004; + + /** + * Flag for use with {@link #createPackageContext}: point all file APIs at + * device-protected storage. + * + * @hide + */ + public static final int CONTEXT_DEVICE_PROTECTED_STORAGE = 0x00000008; + + /** + * Flag for use with {@link #createPackageContext}: point all file APIs at + * credential-protected storage. + * + * @hide + */ + public static final int CONTEXT_CREDENTIAL_PROTECTED_STORAGE = 0x00000010; + + /** + * @hide Used to indicate we should tell the activity manager about the process + * loading this code. + */ + public static final int CONTEXT_REGISTER_PACKAGE = 0x40000000; + + /** + * Return a new Context object for the given application name. This + * Context is the same as what the named application gets when it is + * launched, containing the same resources and class loader. Each call to + * this method returns a new instance of a Context object; Context objects + * are not shared, however they share common state (Resources, ClassLoader, + * etc) so the Context instance itself is fairly lightweight. + * + *

Throws {@link android.content.pm.PackageManager.NameNotFoundException} if there is no + * application with the given package name. + * + *

Throws {@link java.lang.SecurityException} if the Context requested + * can not be loaded into the caller's process for security reasons (see + * {@link #CONTEXT_INCLUDE_CODE} for more information}. + * + * @param packageName Name of the application's package. + * @param flags Option flags. + * + * @return A {@link Context} for the application. + * + * @throws SecurityException   + * @throws PackageManager.NameNotFoundException if there is no application with + * the given package name. + */ + public abstract Context createPackageContext(String packageName, + @CreatePackageOptions int flags) throws PackageManager.NameNotFoundException; + + /** + * Similar to {@link #createPackageContext(String, int)}, but with a + * different {@link UserHandle}. For example, {@link #getContentResolver()} + * will open any {@link Uri} as the given user. + * + * @hide + */ + @SystemApi + @NonNull + public Context createPackageContextAsUser( + @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user) + throws PackageManager.NameNotFoundException { + if (Build.IS_ENG) { + throw new IllegalStateException("createPackageContextAsUser not overridden!"); + } + return this; + } + + /** + * Similar to {@link #createPackageContext(String, int)}, but for the own package with a + * different {@link UserHandle}. For example, {@link #getContentResolver()} + * will open any {@link Uri} as the given user. + * + * @hide + */ + @SystemApi + @NonNull + public Context createContextAsUser(@NonNull UserHandle user, @CreatePackageOptions int flags) { + if (Build.IS_ENG) { + throw new IllegalStateException("createContextAsUser not overridden!"); + } + return this; + } + + /** + * Creates a context given an {@link android.content.pm.ApplicationInfo}. + * + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @UnsupportedAppUsage + public abstract Context createApplicationContext(ApplicationInfo application, + @CreatePackageOptions int flags) throws PackageManager.NameNotFoundException; + + /** + * Return a new Context object for the given split name. The new Context has a ClassLoader and + * Resources object that can access the split's and all of its dependencies' code/resources. + * Each call to this method returns a new instance of a Context object; + * Context objects are not shared, however common state (ClassLoader, other Resources for + * the same split) may be so the Context itself can be fairly lightweight. + * + * @param splitName The name of the split to include, as declared in the split's + * AndroidManifest.xml. + * @return A {@link Context} with the given split's code and/or resources loaded. + */ + public abstract Context createContextForSplit(String splitName) + throws PackageManager.NameNotFoundException; + + /** + * Get the user associated with this context. + * + * @return the user associated with this context + * + * @hide + */ + @NonNull + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @TestApi + public UserHandle getUser() { + return android.os.Process.myUserHandle(); + } + + /** + * Get the user associated with this context + * @hide + */ + @UnsupportedAppUsage + @TestApi + public @UserIdInt int getUserId() { + return android.os.UserHandle.myUserId(); + } + + /** + * Return a new Context object for the current Context but whose resources + * are adjusted to match the given Configuration. Each call to this method + * returns a new instance of a Context object; Context objects are not + * shared, however common state (ClassLoader, other Resources for the + * same configuration) may be so the Context itself can be fairly lightweight. + * + * @param overrideConfiguration A {@link Configuration} specifying what + * values to modify in the base Configuration of the original Context's + * resources. If the base configuration changes (such as due to an + * orientation change), the resources of this context will also change except + * for those that have been explicitly overridden with a value here. + * + * @return A {@link Context} with the given configuration override. + */ + public abstract Context createConfigurationContext( + @NonNull Configuration overrideConfiguration); + + /** + * Return a new Context object for the current Context but whose resources + * are adjusted to match the metrics of the given Display. Each call to this method + * returns a new instance of a Context object; Context objects are not + * shared, however common state (ClassLoader, other Resources for the + * same configuration) may be so the Context itself can be fairly lightweight. + * + * To obtain an instance of a {@link WindowManager} (see {@link #getSystemService(String)}) that + * is configured to show windows on the given display call + * {@link #createWindowContext(int, Bundle)} on the returned display Context or use an + * {@link android.app.Activity}. + * + *

+ * Note that invoking #createDisplayContext(Display) from an UI context is not regarded + * as an UI context. In other words, it is not suggested to access UI components (such as + * obtain a {@link WindowManager} by {@link #getSystemService(String)}) + * from the context created from #createDisplayContext(Display). + *

+ * + * @param display A {@link Display} object specifying the display for whose metrics the + * Context's resources should be tailored. + * + * @return A {@link Context} for the display. + * + * @see #getSystemService(String) + */ + @DisplayContext + public abstract Context createDisplayContext(@NonNull Display display); + + /** + * Creates a Context for a non-activity window. + * + *

+ * A window context is a context that can be used to add non-activity windows, such as + * {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}. A window context + * must be created from a context that has an associated {@link Display}, such as + * {@link android.app.Activity Activity} or a context created with + * {@link #createDisplayContext(Display)}. + * + *

+ * The window context is created with the appropriate {@link Configuration} for the area of the + * display that the windows created with it can occupy; it must be used when + * {@link android.view.LayoutInflater inflating} views, such that they can be inflated with + * proper {@link Resources}. + * + * Below is a sample code to add an application overlay window on the primary display: + *

+     * ...
+     * final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
+     * final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+     * final Context windowContext = anyContext.createDisplayContext(primaryDisplay)
+     *         .createWindowContext(TYPE_APPLICATION_OVERLAY, null);
+     * final View overlayView = Inflater.from(windowContext).inflate(someLayoutXml, null);
+     *
+     * // WindowManager.LayoutParams initialization
+     * ...
+     * // The types used in addView and createWindowContext must match.
+     * mParams.type = TYPE_APPLICATION_OVERLAY;
+     * ...
+     *
+     * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+     * 
+ * + *

+ * This context's configuration and resources are adjusted to an area of the display where + * the windows with provided type will be added. Note that all windows associated with the + * same context will have an affinity and can only be moved together between different displays + * or areas on a display. If there is a need to add different window types, or + * non-associated windows, separate Contexts should be used. + *

+ *

+ * Creating a window context is an expensive operation. Misuse of this API may lead to a huge + * performance drop. The best practice is to use the same window context when possible. + * An approach is to create one window context with specific window type and display and + * use it everywhere it's needed. + *

+ *

+ * After {@link Build.VERSION_CODES#S}, window context provides the capability to receive + * configuration changes for existing token by overriding the + * {@link android.view.WindowManager.LayoutParams#token token} of the + * {@link android.view.WindowManager.LayoutParams} passed in + * {@link WindowManager#addView(View, LayoutParams)}. This is useful when an application needs + * to attach its window to an existing activity for window token sharing use-case. + *

+ *

+ * Note that the window context in {@link Build.VERSION_CODES#R} didn't have this + * capability. This is a no-op for the window context in {@link Build.VERSION_CODES#R}. + *

+ * Below is sample code to attach an existing token to a window context: + *
+     * final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
+     * final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+     * final Context windowContext = anyContext.createWindowContext(primaryDisplay,
+     *         TYPE_APPLICATION, null);
+     *
+     * // Get an existing token.
+     * final IBinder existingToken = activity.getWindow().getAttributes().token;
+     *
+     * // The types used in addView() and createWindowContext() must match.
+     * final WindowManager.LayoutParams params = new WindowManager.LayoutParams(TYPE_APPLICATION);
+     * params.token = existingToken;
+     *
+     * // After WindowManager#addView(), the server side will extract the provided token from
+     * // LayoutParams#token (existingToken in the sample code), and switch to propagate
+     * // configuration changes from the node associated with the provided token.
+     * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+     * 
+ *

+ * After {@link Build.VERSION_CODES#S}, window context provides the capability to listen to its + * {@link Configuration} changes by calling + * {@link #registerComponentCallbacks(ComponentCallbacks)}, while other kinds of {@link Context} + * will register the {@link ComponentCallbacks} to {@link #getApplicationContext() its + * Application context}. Note that window context only propagate + * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} callback. + * {@link ComponentCallbacks#onLowMemory()} or other callbacks in {@link ComponentCallbacks2} + * won't be invoked. + *

+ *

+ * Note that using {@link android.app.Application} or {@link android.app.Service} context for + * UI-related queries may result in layout or continuity issues on devices with variable screen + * sizes (e.g. foldables) or in multi-window modes, since these non-UI contexts may not reflect + * the {@link Configuration} changes for the visual container. + *

+ * @param type Window type in {@link WindowManager.LayoutParams} + * @param options A bundle used to pass window-related options + * @return A {@link Context} that can be used to create + * non-{@link android.app.Activity activity} windows. + * + * @see #getSystemService(String) + * @see #getSystemService(Class) + * @see #WINDOW_SERVICE + * @see #LAYOUT_INFLATER_SERVICE + * @see #WALLPAPER_SERVICE + * @throws UnsupportedOperationException if this {@link Context} does not attach to a display, + * such as {@link android.app.Application Application} or {@link android.app.Service Service}. + */ + @UiContext + @NonNull + public Context createWindowContext(@WindowType int type, @Nullable Bundle options) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Creates a {@code Context} for a non-{@link android.app.Activity activity} window on the given + * {@link Display}. + * + *

+ * Similar to {@link #createWindowContext(int, Bundle)}, but the {@code display} is passed in, + * instead of implicitly using the {@link #getDisplay() original Context's Display}. + *

+ * + * @param display The {@link Display} to associate with + * @param type Window type in {@link WindowManager.LayoutParams} + * @param options A bundle used to pass window-related options. + * @return A {@link Context} that can be used to create + * non-{@link android.app.Activity activity} windows. + * @throws IllegalArgumentException if the {@link Display} is {@code null}. + * + * @see #getSystemService(String) + * @see #getSystemService(Class) + * @see #WINDOW_SERVICE + * @see #LAYOUT_INFLATER_SERVICE + * @see #WALLPAPER_SERVICE + */ + @UiContext + @NonNull + public Context createWindowContext(@NonNull Display display, @WindowType int type, + @SuppressLint("NullableCollection") + @Nullable Bundle options) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Creates a context with specific properties and behaviors. + * + * @param contextParams Parameters for how the new context should behave. + * @return A context with the specified behaviors. + * + * @see ContextParams + */ + @NonNull + public Context createContext(@NonNull ContextParams contextParams) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Return a new Context object for the current Context but attribute to a different tag. + * In complex apps attribution tagging can be used to distinguish between separate logical + * parts. + * + * @param attributionTag The tag or {@code null} to create a context for the default. + * + * @return A {@link Context} that is tagged for the new attribution + * + * @see #getAttributionTag() + */ + public @NonNull Context createAttributionContext(@Nullable String attributionTag) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + // TODO moltmann: remove + /** + * @removed + */ + @Deprecated + public @NonNull Context createFeatureContext(@Nullable String attributionTag) { + return createContext(new ContextParams.Builder(getParams()) + .setAttributionTag(attributionTag) + .build()); + } + + /** + * Return a new Context object for the current Context but whose storage + * APIs are backed by device-protected storage. + *

+ * On devices with direct boot, data stored in this location is encrypted + * with a key tied to the physical device, and it can be accessed + * immediately after the device has booted successfully, both + * before and after the user has authenticated with their + * credentials (such as a lock pattern or PIN). + *

+ * Because device-protected data is available without user authentication, + * you should carefully limit the data you store using this Context. For + * example, storing sensitive authentication tokens or passwords in the + * device-protected area is strongly discouraged. + *

+ * If the underlying device does not have the ability to store + * device-protected and credential-protected data using different keys, then + * both storage areas will become available at the same time. They remain as + * two distinct storage locations on disk, and only the window of + * availability changes. + *

+ * Each call to this method returns a new instance of a Context object; + * Context objects are not shared, however common state (ClassLoader, other + * Resources for the same configuration) may be so the Context itself can be + * fairly lightweight. + * + * @see #isDeviceProtectedStorage() + */ + public abstract Context createDeviceProtectedStorageContext(); + + /** + * Return a new Context object for the current Context but whose storage + * APIs are backed by credential-protected storage. This is the default + * storage area for apps unless + * {@link android.R.attr#defaultToDeviceProtectedStorage} was requested. + *

+ * On devices with direct boot, data stored in this location is encrypted + * with a key tied to user credentials, which can be accessed + * only after the user has entered their credentials (such as a + * lock pattern or PIN). + *

+ * If the underlying device does not have the ability to store + * device-protected and credential-protected data using different keys, then + * both storage areas will become available at the same time. They remain as + * two distinct storage locations on disk, and only the window of + * availability changes. + *

+ * Each call to this method returns a new instance of a Context object; + * Context objects are not shared, however common state (ClassLoader, other + * Resources for the same configuration) may be so the Context itself can be + * fairly lightweight. + * + * @see #isCredentialProtectedStorage() + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + public abstract Context createCredentialProtectedStorageContext(); + + /** + * Creates a UI context with a {@code token}. The users of this API should handle this context's + * configuration changes. + * + * @param token The token to associate with the {@link Resources} + * @param display The display to associate with the token context + * + * @hide + */ + @UiContext + @NonNull + public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Gets the display adjustments holder for this context. This information + * is provided on a per-application or activity basis and is used to simulate lower density + * display metrics for legacy applications and restricted screen sizes. + * + * @param displayId The display id for which to get compatibility info. + * @return The compatibility info holder, or null if not required by the application. + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + public abstract DisplayAdjustments getDisplayAdjustments(int displayId); + + /** + * Get the display this context is associated with. Applications should use this method with + * {@link android.app.Activity} or a context associated with a {@link Display} via + * {@link #createDisplayContext(Display)} to get a display object associated with a Context, or + * {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id. + * @return Returns the {@link Display} object this context is associated with. + * @throws UnsupportedOperationException if the method is called on an instance that is not + * associated with any display. + */ + @Nullable + public Display getDisplay() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * A version of {@link #getDisplay()} that does not perform a Context misuse check to be used by + * legacy APIs. + * TODO(b/149790106): Fix usages and remove. + * @hide + */ + @Nullable + public Display getDisplayNoVerify() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Gets the ID of the display this context is associated with. + * + * @return display ID associated with this {@link Context}. + * @see #getDisplay() + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @TestApi + public abstract int getDisplayId(); + + /** + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + public abstract void updateDisplay(int displayId); + + /** + * Indicates whether this Context is restricted. + * + * @return {@code true} if this Context is restricted, {@code false} otherwise. + * + * @see #CONTEXT_RESTRICTED + */ + public boolean isRestricted() { + return false; + } + + /** + * Indicates if the storage APIs of this Context are backed by + * device-protected storage. + * + * @see #createDeviceProtectedStorageContext() + */ + public abstract boolean isDeviceProtectedStorage(); + + /** + * Indicates if the storage APIs of this Context are backed by + * credential-protected storage. + * + * @see #createCredentialProtectedStorageContext() + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + @SystemApi + public abstract boolean isCredentialProtectedStorage(); + + /** + * Returns true if the context can load unsafe resources, e.g. fonts. + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + public abstract boolean canLoadUnsafeResources(); + + /** + * Returns token if the {@link Context} is a {@link android.app.Activity}. Returns + * {@code null} otherwise. + * + * @hide + */ + @Nullable + public IBinder getActivityToken() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Returns token if the {@link Context} is a {@link android.app.WindowContext}. Returns + * {@code null} otherwise. + * + * @hide + */ + @Nullable + public IBinder getWindowContextToken() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Returns the proper token of a {@link Context}. + * + * If the {@link Context} is an {@link android.app.Activity}, returns + * {@link #getActivityToken()}. If the {@lijnk Context} is a {@link android.app.WindowContext}, + * returns {@link #getWindowContextToken()}. Returns {@code null}, otherwise. + * + * @hide + */ + @Nullable + public static IBinder getToken(@NonNull Context context) { + return context.getActivityToken() != null ? context.getActivityToken() + : context.getWindowContextToken(); + } + + /** + * @hide + */ + @Nullable + public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler, + int flags) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * @hide + */ + public IApplicationThread getIApplicationThread() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * @hide + */ + public Handler getMainThreadHandler() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * @hide + */ + public AutofillClient getAutofillClient() { + return null; + } + + /** + * @hide + */ + public void setAutofillClient(@SuppressWarnings("unused") AutofillClient client) { + } + + /** + * @hide + */ + @Nullable + public ContentCaptureClient getContentCaptureClient() { + return null; + } + + /** + * @hide + */ + public final boolean isAutofillCompatibilityEnabled() { + final AutofillOptions options = getAutofillOptions(); + return options != null && options.compatModeEnabled; + } + + /** + * @hide + */ + @Nullable + public AutofillOptions getAutofillOptions() { + return null; + } + + /** + * @hide + */ + @TestApi + public void setAutofillOptions(@SuppressWarnings("unused") @Nullable AutofillOptions options) { + } + + /** + * Gets the Content Capture options for this context, or {@code null} if it's not allowlisted. + * + * @hide + */ + @Nullable + public ContentCaptureOptions getContentCaptureOptions() { + return null; + } + + /** + * @hide + */ + @TestApi + public void setContentCaptureOptions( + @SuppressWarnings("unused") @Nullable ContentCaptureOptions options) { + } + + /** + * Throws an exception if the Context is using system resources, + * which are non-runtime-overlay-themable and may show inconsistent UI. + * @hide + */ + public void assertRuntimeOverlayThemable() { + // Resources.getSystem() is a singleton and the only Resources not managed by + // ResourcesManager; therefore Resources.getSystem() is not themable. + if (getResources() == Resources.getSystem()) { + throw new IllegalArgumentException("Non-UI context used to display UI; " + + "get a UI context from ActivityThread#getSystemUiContext()"); + } + } + + /** + * Returns {@code true} if the context is a UI context which can access UI components such as + * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or + * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI + * contexts throws {@link android.os.strictmode.Violation} if + * {@link android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled. + *

+ * Examples of UI contexts are + * an {@link android.app.Activity Activity}, a context created from + * {@link #createWindowContext(int, Bundle)} or + * {@link android.inputmethodservice.InputMethodService InputMethodService} + *

+ *

+ * Note that even if it is allowed programmatically, it is not suggested to override this + * method to bypass {@link android.os.strictmode.IncorrectContextUseViolation} verification. + *

+ * + * @see #getDisplay() + * @see #getSystemService(String) + * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse() + */ + public boolean isUiContext() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** + * Called when a {@link Context} is going to be released. + * This method can be overridden to perform the final cleanups, such as release + * {@link BroadcastReceiver} registrations. + * + * @see WindowContext#destroy() + * + * @hide + */ + public void destroy() { } + + /** + * Indicates this {@link Context} has the proper {@link Configuration} to obtain + * {@link android.view.LayoutInflater}, {@link android.view.ViewConfiguration} and + * {@link android.view.GestureDetector}. Generally, all UI contexts, such as + * {@link android.app.Activity} or {@link android.app.WindowContext}, are initialized with base + * configuration. + *

+ * Note that the context created via {@link Context#createConfigurationContext(Configuration)} + * is also regarded as a context that is based on a configuration because the + * configuration is explicitly provided via the API. + *

+ * + * @see #isUiContext() + * @see #createConfigurationContext(Configuration) + * + * @hide + */ + public boolean isConfigurationContext() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } +} diff --git a/code/chapter-06/mikrom_service/IMikRomManager.aidl b/code/chapter-06/mikrom_service/IMikRomManager.aidl new file mode 100644 index 0000000..b934891 --- /dev/null +++ b/code/chapter-06/mikrom_service/IMikRomManager.aidl @@ -0,0 +1,23 @@ +/* //device/java/android/android/os/IPowerManager.aidl +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.os; + +interface IMikRomManager +{ + String hello(); +} diff --git a/code/chapter-06/mikrom_service/MikRomManager.java b/code/chapter-06/mikrom_service/MikRomManager.java new file mode 100644 index 0000000..f72dfab --- /dev/null +++ b/code/chapter-06/mikrom_service/MikRomManager.java @@ -0,0 +1,46 @@ +package android.os; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; +import android.annotation.SystemService; +import android.os.IMikRomManager; + +@SystemService(Context.MIKROM_SERVICE) +public final class MikRomManager { + private static final String TAG = "MikRomManager"; + IMikRomManager mService; + public MikRomManager(IMikRomManager service) { + mService = service; + } + private static MikRomManager sInstance; + /** + *@hide + */ + @NonNull + @UnsupportedAppUsage + public static MikRomManager getInstance() { + synchronized (MikRomManager.class) { + if (sInstance == null) { + + try { + IBinder mikromBinder = ServiceManager.getServiceOrThrow(Context.MIKROM_SERVICE); + IMikRomManager mikromService = IMikRomManager.Stub.asInterface(mikromBinder); + sInstance= new MikRomManager(mikromService); + } catch (ServiceManager.ServiceNotFoundException e) { + throw new IllegalStateException(e); + } + } + return sInstance; + } + } + @Nullable + public String hello(){ + try{ + return mService.hello(); + }catch (RemoteException ex){ + throw ex.rethrowFromSystemServer(); + } + } +} \ No newline at end of file diff --git a/code/chapter-06/mikrom_service/MikRomManagerService.java b/code/chapter-06/mikrom_service/MikRomManagerService.java new file mode 100644 index 0000000..1186ba2 --- /dev/null +++ b/code/chapter-06/mikrom_service/MikRomManagerService.java @@ -0,0 +1,13 @@ +package com.android.server; + +import android.os.RemoteException; +import android.os.IMikRomManager; + +public class MikRomManagerService extends IMikRomManager.Stub { + + private String TAG="MikRomManagerService"; + @Override + public String hello() throws RemoteException{ + return "hello mikrom service"; + } +} \ No newline at end of file diff --git a/code/chapter-06/mikrom_service/SystemServer.java b/code/chapter-06/mikrom_service/SystemServer.java new file mode 100644 index 0000000..2c5d894 --- /dev/null +++ b/code/chapter-06/mikrom_service/SystemServer.java @@ -0,0 +1,3066 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; +import static android.os.IServiceManager.DUMP_FLAG_PROTO; +import static android.os.Process.SYSTEM_UID; +import static android.os.Process.myPid; +import static android.system.OsConstants.O_CLOEXEC; +import static android.system.OsConstants.O_RDONLY; +import static android.view.Display.DEFAULT_DISPLAY; + +import static com.android.server.utils.TimingsTraceAndSlog.SYSTEM_SERVER_TIMING_TAG; + +import android.annotation.NonNull; +import android.annotation.StringRes; +import android.app.ActivityThread; +import android.app.AppCompatCallbacks; +import android.app.ApplicationErrorReport; +import android.app.INotificationManager; +import android.app.SystemServiceRegistry; +import android.app.admin.DevicePolicySafetyChecker; +import android.app.usage.UsageStatsManagerInternal; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.res.Configuration; +import android.content.res.Resources.Theme; +import android.database.sqlite.SQLiteCompatibilityWalFlags; +import android.database.sqlite.SQLiteGlobal; +import android.graphics.GraphicsStatsService; +import android.graphics.Typeface; +import android.hardware.display.DisplayManagerInternal; +import android.net.ConnectivityManager; +import android.net.ConnectivityModuleConnector; +import android.net.NetworkStackClient; +import android.os.BaseBundle; +import android.os.Binder; +import android.os.Build; +import android.os.Debug; +import android.os.Environment; +import android.os.FactoryTest; +import android.os.FileUtils; +import android.os.IBinder; +import android.os.IIncidentManager; +import android.os.Looper; +import android.os.Message; +import android.os.Parcel; +import android.os.PowerManager; +import android.os.Process; +import android.os.ServiceManager; +import android.os.StrictMode; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.storage.IStorageManager; +import android.provider.DeviceConfig; +import android.provider.Settings; +import android.server.ServerProtoEnums; +import android.sysprop.VoldProperties; +import android.system.ErrnoException; +import android.system.Os; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.DisplayMetrics; +import android.util.EventLog; +import android.util.IndentingPrintWriter; +import android.util.Pair; +import android.util.Slog; +import android.util.TimeUtils; +import android.view.contentcapture.ContentCaptureManager; + +import com.android.i18n.timezone.ZoneInfoDb; +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.os.BinderInternal; +import com.android.internal.os.RuntimeInit; +import com.android.internal.policy.AttributeCache; +import com.android.internal.util.ConcurrentUtils; +import com.android.internal.util.EmergencyAffordanceManager; +import com.android.internal.util.FrameworkStatsLog; +import com.android.internal.widget.ILockSettings; +import com.android.server.am.ActivityManagerService; +import com.android.server.appbinding.AppBindingService; +import com.android.server.attention.AttentionManagerService; +import com.android.server.audio.AudioService; +import com.android.server.biometrics.AuthService; +import com.android.server.biometrics.BiometricService; +import com.android.server.biometrics.sensors.face.FaceService; +import com.android.server.biometrics.sensors.fingerprint.FingerprintService; +import com.android.server.biometrics.sensors.iris.IrisService; +import com.android.server.broadcastradio.BroadcastRadioService; +import com.android.server.camera.CameraServiceProxy; +import com.android.server.clipboard.ClipboardService; +import com.android.server.compat.PlatformCompat; +import com.android.server.compat.PlatformCompatNative; +import com.android.server.connectivity.PacProxyService; +import com.android.server.contentcapture.ContentCaptureManagerInternal; +import com.android.server.coverage.CoverageService; +import com.android.server.devicepolicy.DevicePolicyManagerService; +import com.android.server.devicestate.DeviceStateManagerService; +import com.android.server.display.DisplayManagerService; +import com.android.server.display.color.ColorDisplayService; +import com.android.server.dreams.DreamManagerService; +import com.android.server.emergency.EmergencyAffordanceService; +import com.android.server.gpu.GpuService; +import com.android.server.graphics.fonts.FontManagerService; +import com.android.server.hdmi.HdmiControlService; +import com.android.server.incident.IncidentCompanionService; +import com.android.server.input.InputManagerService; +import com.android.server.inputmethod.InputMethodManagerService; +import com.android.server.inputmethod.InputMethodSystemProperty; +import com.android.server.inputmethod.MultiClientInputMethodManagerService; +import com.android.server.integrity.AppIntegrityManagerService; +import com.android.server.lights.LightsService; +import com.android.server.location.LocationManagerService; +import com.android.server.media.MediaRouterService; +import com.android.server.media.metrics.MediaMetricsManagerService; +import com.android.server.media.projection.MediaProjectionManagerService; +import com.android.server.net.NetworkPolicyManagerService; +import com.android.server.net.NetworkStatsService; +import com.android.server.net.watchlist.NetworkWatchlistService; +import com.android.server.notification.NotificationManagerService; +import com.android.server.oemlock.OemLockService; +import com.android.server.om.OverlayManagerService; +import com.android.server.os.BugreportManagerService; +import com.android.server.os.DeviceIdentifiersPolicyService; +import com.android.server.os.NativeTombstoneManagerService; +import com.android.server.os.SchedulingPolicyService; +import com.android.server.people.PeopleService; +import com.android.server.pm.BackgroundDexOptService; +import com.android.server.pm.CrossProfileAppsService; +import com.android.server.pm.DataLoaderManagerService; +import com.android.server.pm.DynamicCodeLoggingService; +import com.android.server.pm.Installer; +import com.android.server.pm.LauncherAppsService; +import com.android.server.pm.OtaDexoptService; +import com.android.server.pm.PackageManagerService; +import com.android.server.pm.ShortcutService; +import com.android.server.pm.UserManagerService; +import com.android.server.pm.dex.SystemServerDexLoadReporter; +import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.policy.AppOpsPolicy; +import com.android.server.policy.PermissionPolicyService; +import com.android.server.policy.PhoneWindowManager; +import com.android.server.policy.role.RoleServicePlatformHelperImpl; +import com.android.server.power.PowerManagerService; +import com.android.server.power.ShutdownThread; +import com.android.server.power.ThermalManagerService; +import com.android.server.power.hint.HintManagerService; +import com.android.server.powerstats.PowerStatsService; +import com.android.server.profcollect.ProfcollectForwardingService; +import com.android.server.recoverysystem.RecoverySystemService; +import com.android.server.restrictions.RestrictionsManagerService; +import com.android.server.role.RoleServicePlatformHelper; +import com.android.server.rotationresolver.RotationResolverManagerService; +import com.android.server.security.FileIntegrityService; +import com.android.server.security.KeyAttestationApplicationIdProviderService; +import com.android.server.security.KeyChainSystemService; +import com.android.server.sensors.SensorService; +import com.android.server.signedconfig.SignedConfigService; +import com.android.server.soundtrigger.SoundTriggerService; +import com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService; +import com.android.server.statusbar.StatusBarManagerService; +import com.android.server.storage.DeviceStorageMonitorService; +import com.android.server.telecom.TelecomLoaderService; +import com.android.server.testharness.TestHarnessModeService; +import com.android.server.textclassifier.TextClassificationManagerService; +import com.android.server.textservices.TextServicesManagerService; +import com.android.server.tracing.TracingServiceProxy; +import com.android.server.trust.TrustManagerService; +import com.android.server.tv.TvInputManagerService; +import com.android.server.tv.TvRemoteService; +import com.android.server.tv.tunerresourcemanager.TunerResourceManagerService; +import com.android.server.twilight.TwilightService; +import com.android.server.uri.UriGrantsManagerService; +import com.android.server.usage.UsageStatsService; +import com.android.server.utils.TimingsTraceAndSlog; +import com.android.server.vibrator.VibratorManagerService; +import com.android.server.vr.VrManagerService; +import com.android.server.webkit.WebViewUpdateService; +import com.android.server.wm.ActivityTaskManagerService; +import com.android.server.wm.WindowManagerGlobalLock; +import com.android.server.wm.WindowManagerService; + +import dalvik.system.VMRuntime; + +import com.google.android.startop.iorap.IorapForwardingService; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.LinkedList; +import java.util.Locale; +import java.util.Timer; +import java.util.TreeSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; + +/** + * Entry point to {@code system_server}. + */ +public final class SystemServer implements Dumpable { + + private static final String TAG = "SystemServer"; + + private static final String ENCRYPTING_STATE = "trigger_restart_min_framework"; + private static final String ENCRYPTED_STATE = "1"; + + private static final long SLOW_DISPATCH_THRESHOLD_MS = 100; + private static final long SLOW_DELIVERY_THRESHOLD_MS = 200; + + /* + * Implementation class names. TODO: Move them to a codegen class or load + * them from the build system somehow. + */ + private static final String BACKUP_MANAGER_SERVICE_CLASS = + "com.android.server.backup.BackupManagerService$Lifecycle"; + private static final String APPWIDGET_SERVICE_CLASS = + "com.android.server.appwidget.AppWidgetService"; + private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS = + "com.android.server.voiceinteraction.VoiceInteractionManagerService"; + private static final String APP_HIBERNATION_SERVICE_CLASS = + "com.android.server.apphibernation.AppHibernationService"; + private static final String PRINT_MANAGER_SERVICE_CLASS = + "com.android.server.print.PrintManagerService"; + private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS = + "com.android.server.companion.CompanionDeviceManagerService"; + private static final String STATS_COMPANION_APEX_PATH = + "/apex/com.android.os.statsd/javalib/service-statsd.jar"; + private static final String SCHEDULING_APEX_PATH = + "/apex/com.android.scheduling/javalib/service-scheduling.jar"; + private static final String REBOOT_READINESS_LIFECYCLE_CLASS = + "com.android.server.scheduling.RebootReadinessManagerService$Lifecycle"; + private static final String CONNECTIVITY_SERVICE_APEX_PATH = + "/apex/com.android.tethering/javalib/service-connectivity.jar"; + private static final String STATS_COMPANION_LIFECYCLE_CLASS = + "com.android.server.stats.StatsCompanion$Lifecycle"; + private static final String STATS_PULL_ATOM_SERVICE_CLASS = + "com.android.server.stats.pull.StatsPullAtomService"; + private static final String USB_SERVICE_CLASS = + "com.android.server.usb.UsbService$Lifecycle"; + private static final String MIDI_SERVICE_CLASS = + "com.android.server.midi.MidiService$Lifecycle"; + private static final String WIFI_APEX_SERVICE_JAR_PATH = + "/apex/com.android.wifi/javalib/service-wifi.jar"; + private static final String WIFI_SERVICE_CLASS = + "com.android.server.wifi.WifiService"; + private static final String WIFI_SCANNING_SERVICE_CLASS = + "com.android.server.wifi.scanner.WifiScanningService"; + private static final String WIFI_RTT_SERVICE_CLASS = + "com.android.server.wifi.rtt.RttService"; + private static final String WIFI_AWARE_SERVICE_CLASS = + "com.android.server.wifi.aware.WifiAwareService"; + private static final String WIFI_P2P_SERVICE_CLASS = + "com.android.server.wifi.p2p.WifiP2pService"; + private static final String LOWPAN_SERVICE_CLASS = + "com.android.server.lowpan.LowpanService"; + private static final String ETHERNET_SERVICE_CLASS = + "com.android.server.ethernet.EthernetService"; + private static final String JOB_SCHEDULER_SERVICE_CLASS = + "com.android.server.job.JobSchedulerService"; + private static final String LOCK_SETTINGS_SERVICE_CLASS = + "com.android.server.locksettings.LockSettingsService$Lifecycle"; + private static final String STORAGE_MANAGER_SERVICE_CLASS = + "com.android.server.StorageManagerService$Lifecycle"; + private static final String STORAGE_STATS_SERVICE_CLASS = + "com.android.server.usage.StorageStatsService$Lifecycle"; + private static final String SEARCH_MANAGER_SERVICE_CLASS = + "com.android.server.search.SearchManagerService$Lifecycle"; + private static final String THERMAL_OBSERVER_CLASS = + "com.google.android.clockwork.ThermalObserver"; + private static final String WEAR_CONNECTIVITY_SERVICE_CLASS = + "com.android.clockwork.connectivity.WearConnectivityService"; + private static final String WEAR_POWER_SERVICE_CLASS = + "com.android.clockwork.power.WearPowerService"; + private static final String WEAR_SIDEKICK_SERVICE_CLASS = + "com.google.android.clockwork.sidekick.SidekickService"; + private static final String WEAR_DISPLAY_SERVICE_CLASS = + "com.google.android.clockwork.display.WearDisplayService"; + private static final String WEAR_LEFTY_SERVICE_CLASS = + "com.google.android.clockwork.lefty.WearLeftyService"; + private static final String WEAR_TIME_SERVICE_CLASS = + "com.google.android.clockwork.time.WearTimeService"; + private static final String WEAR_GLOBAL_ACTIONS_SERVICE_CLASS = + "com.android.clockwork.globalactions.GlobalActionsService"; + private static final String ACCOUNT_SERVICE_CLASS = + "com.android.server.accounts.AccountManagerService$Lifecycle"; + private static final String CONTENT_SERVICE_CLASS = + "com.android.server.content.ContentService$Lifecycle"; + private static final String WALLPAPER_SERVICE_CLASS = + "com.android.server.wallpaper.WallpaperManagerService$Lifecycle"; + private static final String AUTO_FILL_MANAGER_SERVICE_CLASS = + "com.android.server.autofill.AutofillManagerService"; + private static final String CONTENT_CAPTURE_MANAGER_SERVICE_CLASS = + "com.android.server.contentcapture.ContentCaptureManagerService"; + private static final String TRANSLATION_MANAGER_SERVICE_CLASS = + "com.android.server.translation.TranslationManagerService"; + private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS = + "com.android.server.musicrecognition.MusicRecognitionManagerService"; + private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS = + "com.android.server.systemcaptions.SystemCaptionsManagerService"; + private static final String TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS = + "com.android.server.texttospeech.TextToSpeechManagerService"; + private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS = + "com.android.server.timezone.RulesManagerService$Lifecycle"; + private static final String IOT_SERVICE_CLASS = + "com.android.things.server.IoTSystemService"; + private static final String SLICE_MANAGER_SERVICE_CLASS = + "com.android.server.slice.SliceManagerService$Lifecycle"; + private static final String CAR_SERVICE_HELPER_SERVICE_CLASS = + "com.android.internal.car.CarServiceHelperService"; + private static final String TIME_DETECTOR_SERVICE_CLASS = + "com.android.server.timedetector.TimeDetectorService$Lifecycle"; + private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS = + "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle"; + private static final String LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS = + "com.android.server.timezonedetector.location.LocationTimeZoneManagerService$Lifecycle"; + private static final String GNSS_TIME_UPDATE_SERVICE_CLASS = + "com.android.server.timedetector.GnssTimeUpdateService$Lifecycle"; + private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS = + "com.android.server.accessibility.AccessibilityManagerService$Lifecycle"; + private static final String ADB_SERVICE_CLASS = + "com.android.server.adb.AdbService$Lifecycle"; + private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS = + "com.android.server.speech.SpeechRecognitionManagerService"; + private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS = + "com.android.server.appprediction.AppPredictionManagerService"; + private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS = + "com.android.server.contentsuggestions.ContentSuggestionsManagerService"; + private static final String SEARCH_UI_MANAGER_SERVICE_CLASS = + "com.android.server.searchui.SearchUiManagerService"; + private static final String SMARTSPACE_MANAGER_SERVICE_CLASS = + "com.android.server.smartspace.SmartspaceManagerService"; + private static final String DEVICE_IDLE_CONTROLLER_CLASS = + "com.android.server.DeviceIdleController"; + private static final String BLOB_STORE_MANAGER_SERVICE_CLASS = + "com.android.server.blob.BlobStoreManagerService"; + private static final String APP_SEARCH_MANAGER_SERVICE_CLASS = + "com.android.server.appsearch.AppSearchManagerService"; + private static final String ROLLBACK_MANAGER_SERVICE_CLASS = + "com.android.server.rollback.RollbackManagerService"; + private static final String ALARM_MANAGER_SERVICE_CLASS = + "com.android.server.alarm.AlarmManagerService"; + private static final String MEDIA_SESSION_SERVICE_CLASS = + "com.android.server.media.MediaSessionService"; + private static final String MEDIA_RESOURCE_MONITOR_SERVICE_CLASS = + "com.android.server.media.MediaResourceMonitorService"; + private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS = + "com.android.server.ConnectivityServiceInitializer"; + private static final String IP_CONNECTIVITY_METRICS_CLASS = + "com.android.server.connectivity.IpConnectivityMetrics"; + private static final String MEDIA_COMMUNICATION_SERVICE_CLASS = + "com.android.server.media.MediaCommunicationService"; + + private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService"; + private static final String GAME_MANAGER_SERVICE_CLASS = + "com.android.server.app.GameManagerService$Lifecycle"; + private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService"; + + private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; + + private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; + + private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file"; + private static final String BLOCK_MAP_FILE = "/cache/recovery/block.map"; + + // maximum number of binder threads used for system_server + // will be higher than the system default + private static final int sMaxBinderThreads = 31; + + /** + * Default theme used by the system context. This is used to style system-provided dialogs, such + * as the Power Off dialog, and other visual content. + */ + private static final int DEFAULT_SYSTEM_THEME = + com.android.internal.R.style.Theme_DeviceDefault_System; + + private final int mFactoryTestMode; + private Timer mProfilerSnapshotTimer; + + private Context mSystemContext; + private SystemServiceManager mSystemServiceManager; + + // TODO: remove all of these references by improving dependency resolution and boot phases + private PowerManagerService mPowerManagerService; + private ActivityManagerService mActivityManagerService; + private WindowManagerGlobalLock mWindowManagerGlobalLock; + private WebViewUpdateService mWebViewUpdateService; + private DisplayManagerService mDisplayManagerService; + private PackageManagerService mPackageManagerService; + private PackageManager mPackageManager; + private ContentResolver mContentResolver; + private EntropyMixer mEntropyMixer; + private DataLoaderManagerService mDataLoaderManagerService; + private long mIncrementalServiceHandle = 0; + + private boolean mOnlyCore; + private boolean mFirstBoot; + private final int mStartCount; + private final boolean mRuntimeRestart; + private final long mRuntimeStartElapsedTime; + private final long mRuntimeStartUptime; + + private static final String START_HIDL_SERVICES = "StartHidlServices"; + private static final String START_BLOB_STORE_SERVICE = "startBlobStoreManagerService"; + + private static final String SYSPROP_START_COUNT = "sys.system_server.start_count"; + private static final String SYSPROP_START_ELAPSED = "sys.system_server.start_elapsed"; + private static final String SYSPROP_START_UPTIME = "sys.system_server.start_uptime"; + + private Future mZygotePreload; + private Future mBlobStoreServiceStart; + + private final SystemServerDumper mDumper = new SystemServerDumper(); + + /** + * The pending WTF to be logged into dropbox. + */ + private static LinkedList> sPendingWtfs; + + /** Start the IStats services. This is a blocking call and can take time. */ + private static native void startIStatsService(); + + /** + * Start the memtrack proxy service. + */ + private static native void startMemtrackProxyService(); + + /** + * Start all HIDL services that are run inside the system server. This may take some time. + */ + private static native void startHidlServices(); + + /** + * Mark this process' heap as profileable. Only for debug builds. + */ + private static native void initZygoteChildHeapProfiling(); + + private static final String SYSPROP_FDTRACK_ENABLE_THRESHOLD = + "persist.sys.debug.fdtrack_enable_threshold"; + private static final String SYSPROP_FDTRACK_ABORT_THRESHOLD = + "persist.sys.debug.fdtrack_abort_threshold"; + private static final String SYSPROP_FDTRACK_INTERVAL = + "persist.sys.debug.fdtrack_interval"; + + private static int getMaxFd() { + FileDescriptor fd = null; + try { + fd = Os.open("/dev/null", O_RDONLY | O_CLOEXEC, 0); + return fd.getInt$(); + } catch (ErrnoException ex) { + Slog.e("System", "Failed to get maximum fd: " + ex); + } finally { + if (fd != null) { + try { + Os.close(fd); + } catch (ErrnoException ex) { + // If Os.close threw, something went horribly wrong. + throw new RuntimeException(ex); + } + } + } + + return Integer.MAX_VALUE; + } + + private static native void fdtrackAbort(); + + private static final File HEAP_DUMP_PATH = new File("/data/system/heapdump/"); + private static final int MAX_HEAP_DUMPS = 2; + + /** + * Dump system_server's heap. + * + * For privacy reasons, these aren't automatically pulled into bugreports: + * they must be manually pulled by the user. + */ + private static void dumpHprof() { + // hprof dumps are rather large, so ensure we don't fill the disk by generating + // hundreds of these that will live forever. + TreeSet existingTombstones = new TreeSet<>(); + for (File file : HEAP_DUMP_PATH.listFiles()) { + if (!file.isFile()) { + continue; + } + if (!file.getName().startsWith("fdtrack-")) { + continue; + } + existingTombstones.add(file); + } + if (existingTombstones.size() >= MAX_HEAP_DUMPS) { + for (int i = 0; i < MAX_HEAP_DUMPS - 1; ++i) { + // Leave the newest `MAX_HEAP_DUMPS - 1` tombstones in place. + existingTombstones.pollLast(); + } + for (File file : existingTombstones) { + if (!file.delete()) { + Slog.w("System", "Failed to clean up hprof " + file); + } + } + } + + try { + String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); + String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof"; + Debug.dumpHprofData(filename); + } catch (IOException ex) { + Slog.e("System", "Failed to dump fdtrack hprof", ex); + } + } + + /** + * Spawn a thread that monitors for fd leaks. + */ + private static void spawnFdLeakCheckThread() { + final int enableThreshold = SystemProperties.getInt(SYSPROP_FDTRACK_ENABLE_THRESHOLD, 1024); + final int abortThreshold = SystemProperties.getInt(SYSPROP_FDTRACK_ABORT_THRESHOLD, 2048); + final int checkInterval = SystemProperties.getInt(SYSPROP_FDTRACK_INTERVAL, 120); + + new Thread(() -> { + boolean enabled = false; + long nextWrite = 0; + + while (true) { + int maxFd = getMaxFd(); + if (maxFd > enableThreshold) { + // Do a manual GC to clean up fds that are hanging around as garbage. + System.gc(); + System.runFinalization(); + maxFd = getMaxFd(); + } + + if (maxFd > enableThreshold && !enabled) { + Slog.i("System", "fdtrack enable threshold reached, enabling"); + FrameworkStatsLog.write(FrameworkStatsLog.FDTRACK_EVENT_OCCURRED, + FrameworkStatsLog.FDTRACK_EVENT_OCCURRED__EVENT__ENABLED, + maxFd); + + System.loadLibrary("fdtrack"); + enabled = true; + } else if (maxFd > abortThreshold) { + Slog.i("System", "fdtrack abort threshold reached, dumping and aborting"); + FrameworkStatsLog.write(FrameworkStatsLog.FDTRACK_EVENT_OCCURRED, + FrameworkStatsLog.FDTRACK_EVENT_OCCURRED__EVENT__ABORTING, + maxFd); + + dumpHprof(); + fdtrackAbort(); + } else { + // Limit this to once per hour. + long now = SystemClock.elapsedRealtime(); + if (now > nextWrite) { + nextWrite = now + 60 * 60 * 1000; + FrameworkStatsLog.write(FrameworkStatsLog.FDTRACK_EVENT_OCCURRED, + enabled ? FrameworkStatsLog.FDTRACK_EVENT_OCCURRED__EVENT__ENABLED + : FrameworkStatsLog.FDTRACK_EVENT_OCCURRED__EVENT__DISABLED, + maxFd); + } + } + + try { + Thread.sleep(checkInterval * 1000); + } catch (InterruptedException ex) { + continue; + } + } + }).start(); + } + + /** + * Start native Incremental Service and get its handle. + */ + private static native long startIncrementalService(); + + /** + * Inform Incremental Service that system is ready. + */ + private static native void setIncrementalServiceSystemReady(long incrementalServiceHandle); + + /** + * The main entry point from zygote. + */ + public static void main(String[] args) { + new SystemServer().run(); + } + + public SystemServer() { + // Check for factory test mode. + mFactoryTestMode = FactoryTest.getMode(); + + // Record process start information. + // Note SYSPROP_START_COUNT will increment by *2* on a FDE device when it fully boots; + // one for the password screen, second for the actual boot. + mStartCount = SystemProperties.getInt(SYSPROP_START_COUNT, 0) + 1; + mRuntimeStartElapsedTime = SystemClock.elapsedRealtime(); + mRuntimeStartUptime = SystemClock.uptimeMillis(); + Process.setStartTimes(mRuntimeStartElapsedTime, mRuntimeStartUptime); + + // Remember if it's runtime restart(when sys.boot_completed is already set) or reboot + // We don't use "mStartCount > 1" here because it'll be wrong on a FDE device. + // TODO: mRuntimeRestart will *not* be set to true if the proccess crashes before + // sys.boot_completed is set. Fix it. + mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed")); + } + + @Override + public void dump(IndentingPrintWriter pw, String[] args) { + pw.printf("Runtime restart: %b\n", mRuntimeRestart); + pw.printf("Start count: %d\n", mStartCount); + pw.print("Runtime start-up time: "); + TimeUtils.formatDuration(mRuntimeStartUptime, pw); pw.println(); + pw.print("Runtime start-elapsed time: "); + TimeUtils.formatDuration(mRuntimeStartElapsedTime, pw); pw.println(); + } + + private final class SystemServerDumper extends Binder { + + @GuardedBy("mDumpables") + private final ArrayMap mDumpables = new ArrayMap<>(4); + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + final boolean hasArgs = args != null && args.length > 0; + + synchronized (mDumpables) { + if (hasArgs && "--list".equals(args[0])) { + final int dumpablesSize = mDumpables.size(); + for (int i = 0; i < dumpablesSize; i++) { + pw.println(mDumpables.keyAt(i)); + } + return; + } + + if (hasArgs && "--name".equals(args[0])) { + if (args.length < 2) { + pw.println("Must pass at least one argument to --name"); + return; + } + final String name = args[1]; + final Dumpable dumpable = mDumpables.get(name); + if (dumpable == null) { + pw.printf("No dummpable named %s\n", name); + return; + } + + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { + // Strip --name DUMPABLE from args + final String[] actualArgs = Arrays.copyOfRange(args, 2, args.length); + dumpable.dump(ipw, actualArgs); + } + return; + } + + final int dumpablesSize = mDumpables.size(); + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { + for (int i = 0; i < dumpablesSize; i++) { + final Dumpable dumpable = mDumpables.valueAt(i); + ipw.printf("%s:\n", dumpable.getDumpableName()); + ipw.increaseIndent(); + dumpable.dump(ipw, args); + ipw.decreaseIndent(); + ipw.println(); + } + } + } + } + + private void addDumpable(@NonNull Dumpable dumpable) { + synchronized (mDumpables) { + mDumpables.put(dumpable.getDumpableName(), dumpable); + } + } + } + + private void run() { + TimingsTraceAndSlog t = new TimingsTraceAndSlog(); + try { + t.traceBegin("InitBeforeStartServices"); + + // Record the process start information in sys props. + SystemProperties.set(SYSPROP_START_COUNT, String.valueOf(mStartCount)); + SystemProperties.set(SYSPROP_START_ELAPSED, String.valueOf(mRuntimeStartElapsedTime)); + SystemProperties.set(SYSPROP_START_UPTIME, String.valueOf(mRuntimeStartUptime)); + + EventLog.writeEvent(EventLogTags.SYSTEM_SERVER_START, + mStartCount, mRuntimeStartUptime, mRuntimeStartElapsedTime); + + // + // Default the timezone property to GMT if not set. + // + String timezoneProperty = SystemProperties.get("persist.sys.timezone"); + if (!isValidTimeZoneId(timezoneProperty)) { + Slog.w(TAG, "persist.sys.timezone is not valid (" + timezoneProperty + + "); setting to GMT."); + SystemProperties.set("persist.sys.timezone", "GMT"); + } + + // If the system has "persist.sys.language" and friends set, replace them with + // "persist.sys.locale". Note that the default locale at this point is calculated + // using the "-Duser.locale" command line flag. That flag is usually populated by + // AndroidRuntime using the same set of system properties, but only the system_server + // and system apps are allowed to set them. + // + // NOTE: Most changes made here will need an equivalent change to + // core/jni/AndroidRuntime.cpp + if (!SystemProperties.get("persist.sys.language").isEmpty()) { + final String languageTag = Locale.getDefault().toLanguageTag(); + + SystemProperties.set("persist.sys.locale", languageTag); + SystemProperties.set("persist.sys.language", ""); + SystemProperties.set("persist.sys.country", ""); + SystemProperties.set("persist.sys.localevar", ""); + } + + // The system server should never make non-oneway calls + Binder.setWarnOnBlocking(true); + // The system server should always load safe labels + PackageItemInfo.forceSafeLabels(); + + // Default to FULL within the system server. + SQLiteGlobal.sDefaultSyncMode = SQLiteGlobal.SYNC_MODE_FULL; + + // Deactivate SQLiteCompatibilityWalFlags until settings provider is initialized + SQLiteCompatibilityWalFlags.init(null); + + // Here we go! + Slog.i(TAG, "Entered the Android system server!"); + final long uptimeMillis = SystemClock.elapsedRealtime(); + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, uptimeMillis); + if (!mRuntimeRestart) { + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED, + FrameworkStatsLog + .BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SYSTEM_SERVER_INIT_START, + uptimeMillis); + } + + // In case the runtime switched since last boot (such as when + // the old runtime was removed in an OTA), set the system + // property so that it is in sync. We can't do this in + // libnativehelper's JniInvocation::Init code where we already + // had to fallback to a different runtime because it is + // running as root and we need to be the system user to set + // the property. http://b/11463182 + SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary()); + + // Mmmmmm... more memory! + VMRuntime.getRuntime().clearGrowthLimit(); + + // Some devices rely on runtime fingerprint generation, so make sure + // we've defined it before booting further. + Build.ensureFingerprintProperty(); + + // Within the system server, it is an error to access Environment paths without + // explicitly specifying a user. + Environment.setUserRequired(true); + + // Within the system server, any incoming Bundles should be defused + // to avoid throwing BadParcelableException. + BaseBundle.setShouldDefuse(true); + + // Within the system server, when parceling exceptions, include the stack trace + Parcel.setStackTraceParceling(true); + + // Ensure binder calls into the system always run at foreground priority. + BinderInternal.disableBackgroundScheduling(true); + + // Increase the number of binder threads in system_server + BinderInternal.setMaxThreads(sMaxBinderThreads); + + // Prepare the main looper thread (this thread). + android.os.Process.setThreadPriority( + android.os.Process.THREAD_PRIORITY_FOREGROUND); + android.os.Process.setCanSelfBackground(false); + Looper.prepareMainLooper(); + Looper.getMainLooper().setSlowLogThresholdMs( + SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS); + + SystemServiceRegistry.sEnableServiceNotFoundWtf = true; + + // Initialize native services. + System.loadLibrary("android_servers"); + + // Allow heap / perf profiling. + initZygoteChildHeapProfiling(); + + // Debug builds - spawn a thread to monitor for fd leaks. + if (Build.IS_DEBUGGABLE) { + spawnFdLeakCheckThread(); + } + + // Check whether we failed to shut down last time we tried. + // This call may not return. + performPendingShutdown(); + + // Initialize the system context. + createSystemContext(); + + // Call per-process mainline module initialization. + ActivityThread.initializeMainlineModules(); + + // Sets the dumper service + ServiceManager.addService("system_server_dumper", mDumper); + mDumper.addDumpable(this); + + // Create the system service manager. + mSystemServiceManager = new SystemServiceManager(mSystemContext); + mSystemServiceManager.setStartInfo(mRuntimeRestart, + mRuntimeStartElapsedTime, mRuntimeStartUptime); + mDumper.addDumpable(mSystemServiceManager); + + LocalServices.addService(SystemServiceManager.class, mSystemServiceManager); + // Prepare the thread pool for init tasks that can be parallelized + SystemServerInitThreadPool tp = SystemServerInitThreadPool.start(); + mDumper.addDumpable(tp); + + // Load preinstalled system fonts for system server, so that WindowManagerService, etc + // can start using Typeface. Note that fonts are required not only for text rendering, + // but also for some text operations (e.g. TextUtils.makeSafeForPresentation()). + if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + Typeface.loadPreinstalledSystemFontMap(); + } + + // Attach JVMTI agent if this is a debuggable build and the system property is set. + if (Build.IS_DEBUGGABLE) { + // Property is of the form "library_path=parameters". + String jvmtiAgent = SystemProperties.get("persist.sys.dalvik.jvmtiagent"); + if (!jvmtiAgent.isEmpty()) { + int equalIndex = jvmtiAgent.indexOf('='); + String libraryPath = jvmtiAgent.substring(0, equalIndex); + String parameterList = + jvmtiAgent.substring(equalIndex + 1, jvmtiAgent.length()); + // Attach the agent. + try { + Debug.attachJvmtiAgent(libraryPath, parameterList, null); + } catch (Exception e) { + Slog.e("System", "*************************************************"); + Slog.e("System", "********** Failed to load jvmti plugin: " + jvmtiAgent); + } + } + } + } finally { + t.traceEnd(); // InitBeforeStartServices + } + + // Setup the default WTF handler + RuntimeInit.setDefaultApplicationWtfHandler(SystemServer::handleEarlySystemWtf); + + // Start services. + try { + t.traceBegin("StartServices"); + startBootstrapServices(t); + startCoreServices(t); + startOtherServices(t); + } catch (Throwable ex) { + Slog.e("System", "******************************************"); + Slog.e("System", "************ Failure starting system services", ex); + throw ex; + } finally { + t.traceEnd(); // StartServices + } + + StrictMode.initVmDefaults(null); + + if (!mRuntimeRestart && !isFirstBootOrUpgrade()) { + final long uptimeMillis = SystemClock.elapsedRealtime(); + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED, + FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SYSTEM_SERVER_READY, + uptimeMillis); + final long maxUptimeMillis = 60 * 1000; + if (uptimeMillis > maxUptimeMillis) { + Slog.wtf(SYSTEM_SERVER_TIMING_TAG, + "SystemServer init took too long. uptimeMillis=" + uptimeMillis); + } + } + + // Loop forever. + Looper.loop(); + throw new RuntimeException("Main thread loop unexpectedly exited"); + } + + private static boolean isValidTimeZoneId(String timezoneProperty) { + return timezoneProperty != null + && !timezoneProperty.isEmpty() + && ZoneInfoDb.getInstance().hasTimeZone(timezoneProperty); + } + + private boolean isFirstBootOrUpgrade() { + return mPackageManagerService.isFirstBoot() || mPackageManagerService.isDeviceUpgrading(); + } + + private void reportWtf(String msg, Throwable e) { + Slog.w(TAG, "***********************************************"); + Slog.wtf(TAG, "BOOT FAILURE " + msg, e); + } + + private void performPendingShutdown() { + final String shutdownAction = SystemProperties.get( + ShutdownThread.SHUTDOWN_ACTION_PROPERTY, ""); + if (shutdownAction != null && shutdownAction.length() > 0) { + boolean reboot = (shutdownAction.charAt(0) == '1'); + + final String reason; + if (shutdownAction.length() > 1) { + reason = shutdownAction.substring(1, shutdownAction.length()); + } else { + reason = null; + } + + // If it's a pending reboot into recovery to apply an update, + // always make sure uncrypt gets executed properly when needed. + // If '/cache/recovery/block.map' hasn't been created, stop the + // reboot which will fail for sure, and get a chance to capture a + // bugreport when that's still feasible. (Bug: 26444951) + if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) { + File packageFile = new File(UNCRYPT_PACKAGE_FILE); + if (packageFile.exists()) { + String filename = null; + try { + filename = FileUtils.readTextFile(packageFile, 0, null); + } catch (IOException e) { + Slog.e(TAG, "Error reading uncrypt package file", e); + } + + if (filename != null && filename.startsWith("/data")) { + if (!new File(BLOCK_MAP_FILE).exists()) { + Slog.e(TAG, "Can't find block map file, uncrypt failed or " + + "unexpected runtime restart?"); + return; + } + } + } + } + Runnable runnable = new Runnable() { + @Override + public void run() { + synchronized (this) { + ShutdownThread.rebootOrShutdown(null, reboot, reason); + } + } + }; + + // ShutdownThread must run on a looper capable of displaying the UI. + Message msg = Message.obtain(UiThread.getHandler(), runnable); + msg.setAsynchronous(true); + UiThread.getHandler().sendMessage(msg); + + } + } + + private void createSystemContext() { + ActivityThread activityThread = ActivityThread.systemMain(); + mSystemContext = activityThread.getSystemContext(); + mSystemContext.setTheme(DEFAULT_SYSTEM_THEME); + + final Context systemUiContext = activityThread.getSystemUiContext(); + systemUiContext.setTheme(DEFAULT_SYSTEM_THEME); + } + + /** + * Starts the small tangle of critical services that are needed to get the system off the + * ground. These services have complex mutual dependencies which is why we initialize them all + * in one place here. Unless your service is also entwined in these dependencies, it should be + * initialized in one of the other functions. + */ + private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) { + t.traceBegin("startBootstrapServices"); + + // Start the watchdog as early as possible so we can crash the system server + // if we deadlock during early boot + t.traceBegin("StartWatchdog"); + final Watchdog watchdog = Watchdog.getInstance(); + watchdog.start(); + t.traceEnd(); + + Slog.i(TAG, "Reading configuration..."); + final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig"; + t.traceBegin(TAG_SYSTEM_CONFIG); + SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG); + t.traceEnd(); + + // Platform compat service is used by ActivityManagerService, PackageManagerService, and + // possibly others in the future. b/135010838. + t.traceBegin("PlatformCompat"); + PlatformCompat platformCompat = new PlatformCompat(mSystemContext); + ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat); + ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE, + new PlatformCompatNative(platformCompat)); + AppCompatCallbacks.install(new long[0]); + t.traceEnd(); + + // FileIntegrityService responds to requests from apps and the system. It needs to run after + // the source (i.e. keystore) is ready, and before the apps (or the first customer in the + // system) run. + t.traceBegin("StartFileIntegrityService"); + mSystemServiceManager.startService(FileIntegrityService.class); + t.traceEnd(); + + // Wait for installd to finish starting up so that it has a chance to + // create critical directories such as /data/user with the appropriate + // permissions. We need this to complete before we initialize other services. + t.traceBegin("StartInstaller"); + Installer installer = mSystemServiceManager.startService(Installer.class); + t.traceEnd(); + + // In some cases after launching an app we need to access device identifiers, + // therefore register the device identifier policy before the activity manager. + t.traceBegin("DeviceIdentifiersPolicyService"); + mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class); + t.traceEnd(); + + // Uri Grants Manager. + t.traceBegin("UriGrantsManagerService"); + mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartPowerStatsService"); + // Tracks rail data to be used for power statistics. + mSystemServiceManager.startService(PowerStatsService.class); + t.traceEnd(); + + t.traceBegin("StartIStatsService"); + startIStatsService(); + t.traceEnd(); + + // Start MemtrackProxyService before ActivityManager, so that early calls + // to Memtrack::getMemory() don't fail. + t.traceBegin("MemtrackProxyService"); + startMemtrackProxyService(); + t.traceEnd(); + + // Activity manager runs the show. + t.traceBegin("StartActivityManager"); + // TODO: Might need to move after migration to WM. + ActivityTaskManagerService atm = mSystemServiceManager.startService( + ActivityTaskManagerService.Lifecycle.class).getService(); + mActivityManagerService = ActivityManagerService.Lifecycle.startService( + mSystemServiceManager, atm); + mActivityManagerService.setSystemServiceManager(mSystemServiceManager); + mActivityManagerService.setInstaller(installer); + mWindowManagerGlobalLock = atm.getGlobalLock(); + t.traceEnd(); + + // Data loader manager service needs to be started before package manager + t.traceBegin("StartDataLoaderManagerService"); + mDataLoaderManagerService = mSystemServiceManager.startService( + DataLoaderManagerService.class); + t.traceEnd(); + + // Incremental service needs to be started before package manager + t.traceBegin("StartIncrementalService"); + mIncrementalServiceHandle = startIncrementalService(); + t.traceEnd(); + + // Power manager needs to be started early because other services need it. + // Native daemons may be watching for it to be registered so it must be ready + // to handle incoming binder calls immediately (including being able to verify + // the permissions for those calls). + t.traceBegin("StartPowerManager"); + mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class); + t.traceEnd(); + + t.traceBegin("StartThermalManager"); + mSystemServiceManager.startService(ThermalManagerService.class); + t.traceEnd(); + + t.traceBegin("StartHintManager"); + mSystemServiceManager.startService(HintManagerService.class); + t.traceEnd(); + + // Now that the power manager has been started, let the activity manager + // initialize power management features. + t.traceBegin("InitPowerManagement"); + mActivityManagerService.initPowerManagement(); + t.traceEnd(); + + // Bring up recovery system in case a rescue party needs a reboot + t.traceBegin("StartRecoverySystemService"); + mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class); + t.traceEnd(); + + // Now that we have the bare essentials of the OS up and running, take + // note that we just booted, which might send out a rescue party if + // we're stuck in a runtime restart loop. + RescueParty.registerHealthObserver(mSystemContext); + PackageWatchdog.getInstance(mSystemContext).noteBoot(); + + // Manages LEDs and display backlight so we need it to bring up the display. + t.traceBegin("StartLightsService"); + mSystemServiceManager.startService(LightsService.class); + t.traceEnd(); + + t.traceBegin("StartSidekickService"); + // Package manager isn't started yet; need to use SysProp not hardware feature + if (SystemProperties.getBoolean("config.enable_sidekick_graphics", false)) { + mSystemServiceManager.startService(WEAR_SIDEKICK_SERVICE_CLASS); + } + t.traceEnd(); + + // Display manager is needed to provide display metrics before package manager + // starts up. + t.traceBegin("StartDisplayManager"); + mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class); + t.traceEnd(); + + // We need the default display before we can initialize the package manager. + t.traceBegin("WaitForDisplay"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + t.traceEnd(); + + // Only run "core" apps if we're encrypting the device. + String cryptState = VoldProperties.decrypt().orElse(""); + if (ENCRYPTING_STATE.equals(cryptState)) { + Slog.w(TAG, "Detected encryption in progress - only parsing core apps"); + mOnlyCore = true; + } else if (ENCRYPTED_STATE.equals(cryptState)) { + Slog.w(TAG, "Device encrypted - only parsing core apps"); + mOnlyCore = true; + } + + // Start the package manager. + if (!mRuntimeRestart) { + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED, + FrameworkStatsLog + .BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__PACKAGE_MANAGER_INIT_START, + SystemClock.elapsedRealtime()); + } + + t.traceBegin("StartDomainVerificationService"); + DomainVerificationService domainVerificationService = new DomainVerificationService( + mSystemContext, SystemConfig.getInstance(), platformCompat); + mSystemServiceManager.startService(domainVerificationService); + t.traceEnd(); + + t.traceBegin("StartPackageManagerService"); + try { + Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain"); + mPackageManagerService = PackageManagerService.main(mSystemContext, installer, + domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, + mOnlyCore); + } finally { + Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain"); + } + + // Now that the package manager has started, register the dex load reporter to capture any + // dex files loaded by system server. + // These dex files will be optimized by the BackgroundDexOptService. + SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService); + + mFirstBoot = mPackageManagerService.isFirstBoot(); + mPackageManager = mSystemContext.getPackageManager(); + t.traceEnd(); + if (!mRuntimeRestart && !isFirstBootOrUpgrade()) { + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED, + FrameworkStatsLog + .BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__PACKAGE_MANAGER_INIT_READY, + SystemClock.elapsedRealtime()); + } + // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename + // A/B artifacts after boot, before anything else might touch/need them. + // Note: this isn't needed during decryption (we don't have /data anyways). + if (!mOnlyCore) { + boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt", + false); + if (!disableOtaDexopt) { + t.traceBegin("StartOtaDexOptService"); + try { + Watchdog.getInstance().pauseWatchingCurrentThread("moveab"); + OtaDexoptService.main(mSystemContext, mPackageManagerService); + } catch (Throwable e) { + reportWtf("starting OtaDexOptService", e); + } finally { + Watchdog.getInstance().resumeWatchingCurrentThread("moveab"); + t.traceEnd(); + } + } + } + + t.traceBegin("StartUserManagerService"); + mSystemServiceManager.startService(UserManagerService.LifeCycle.class); + t.traceEnd(); + + // Initialize attribute cache used to cache resources from packages. + t.traceBegin("InitAttributerCache"); + AttributeCache.init(mSystemContext); + t.traceEnd(); + + // Set up the Application instance for the system process and get started. + t.traceBegin("SetSystemProcess"); + mActivityManagerService.setSystemProcess(); + t.traceEnd(); + + // The package receiver depends on the activity service in order to get registered. + platformCompat.registerPackageReceiver(mSystemContext); + + // Complete the watchdog setup with an ActivityManager instance and listen for reboots + // Do this only after the ActivityManagerService is properly started as a system process + t.traceBegin("InitWatchdog"); + watchdog.init(mSystemContext, mActivityManagerService); + t.traceEnd(); + + // DisplayManagerService needs to setup android.display scheduling related policies + // since setSystemProcess() would have overridden policies due to setProcessGroup + mDisplayManagerService.setupSchedulerPolicies(); + + // Manages Overlay packages + t.traceBegin("StartOverlayManagerService"); + mSystemServiceManager.startService(new OverlayManagerService(mSystemContext)); + t.traceEnd(); + + t.traceBegin("StartSensorPrivacyService"); + mSystemServiceManager.startService(new SensorPrivacyService(mSystemContext)); + t.traceEnd(); + + if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) { + // DisplayManager needs the overlay immediately. + mActivityManagerService.updateSystemUiContext(); + LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged(); + } + + // The sensor service needs access to package manager service, app ops + // service, and permissions service, therefore we start it after them. + t.traceBegin("StartSensorService"); + mSystemServiceManager.startService(SensorService.class); + t.traceEnd(); + t.traceEnd(); // startBootstrapServices + } + + /** + * Starts some essential services that are not tangled up in the bootstrap process. + */ + private void startCoreServices(@NonNull TimingsTraceAndSlog t) { + t.traceBegin("startCoreServices"); + + // Service for system config + t.traceBegin("StartSystemConfigService"); + mSystemServiceManager.startService(SystemConfigService.class); + t.traceEnd(); + + t.traceBegin("StartBatteryService"); + // Tracks the battery level. Requires LightService. + mSystemServiceManager.startService(BatteryService.class); + t.traceEnd(); + + // Tracks application usage stats. + t.traceBegin("StartUsageService"); + mSystemServiceManager.startService(UsageStatsService.class); + mActivityManagerService.setUsageStatsManager( + LocalServices.getService(UsageStatsManagerInternal.class)); + t.traceEnd(); + + // Tracks whether the updatable WebView is in a ready state and watches for update installs. + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { + t.traceBegin("StartWebViewUpdateService"); + mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class); + t.traceEnd(); + } + + // Tracks and caches the device state. + t.traceBegin("StartCachedDeviceStateService"); + mSystemServiceManager.startService(CachedDeviceStateService.class); + t.traceEnd(); + + // Tracks cpu time spent in binder calls + t.traceBegin("StartBinderCallsStatsService"); + mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class); + t.traceEnd(); + + // Tracks time spent in handling messages in handlers. + t.traceBegin("StartLooperStatsService"); + mSystemServiceManager.startService(LooperStatsService.Lifecycle.class); + t.traceEnd(); + + // Manages apk rollbacks. + t.traceBegin("StartRollbackManagerService"); + mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + // Tracks native tombstones. + t.traceBegin("StartNativeTombstoneManagerService"); + mSystemServiceManager.startService(NativeTombstoneManagerService.class); + t.traceEnd(); + + // Service to capture bugreports. + t.traceBegin("StartBugreportManagerService"); + mSystemServiceManager.startService(BugreportManagerService.class); + t.traceEnd(); + + // Serivce for GPU and GPU driver. + t.traceBegin("GpuService"); + mSystemServiceManager.startService(GpuService.class); + t.traceEnd(); + + t.traceEnd(); // startCoreServices + } + + /** + * Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized. + */ + private void startOtherServices(@NonNull TimingsTraceAndSlog t) { + t.traceBegin("startOtherServices"); + + final Context context = mSystemContext; + DynamicSystemService dynamicSystem = null; + IStorageManager storageManager = null; + NetworkManagementService networkManagement = null; + IpSecService ipSecService = null; + VpnManagerService vpnManager = null; + VcnManagementService vcnManagement = null; + NetworkStatsService networkStats = null; + NetworkPolicyManagerService networkPolicy = null; + NsdService serviceDiscovery = null; + WindowManagerService wm = null; + SerialService serial = null; + NetworkTimeUpdateService networkTimeUpdater = null; + InputManagerService inputManager = null; + TelephonyRegistry telephonyRegistry = null; + ConsumerIrService consumerIr = null; + MmsServiceBroker mmsService = null; + HardwarePropertiesManagerService hardwarePropertiesService = null; + PacProxyService pacProxyService = null; + + boolean disableSystemTextClassifier = SystemProperties.getBoolean( + "config.disable_systemtextclassifier", false); + + boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", + false); + boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice", + false); + boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false); + + boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1"); + + boolean isWatch = context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WATCH); + + boolean isArc = context.getPackageManager().hasSystemFeature( + "org.chromium.arc"); + + boolean enableVrService = context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE); + + // For debugging RescueParty + if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_system", false)) { + throw new RuntimeException(); + } + + try { + final String SECONDARY_ZYGOTE_PRELOAD = "SecondaryZygotePreload"; + // We start the preload ~1s before the webview factory preparation, to + // ensure that it completes before the 32 bit relro process is forked + // from the zygote. In the event that it takes too long, the webview + // RELRO process will block, but it will do so without holding any locks. + mZygotePreload = SystemServerInitThreadPool.submit(() -> { + try { + Slog.i(TAG, SECONDARY_ZYGOTE_PRELOAD); + TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); + traceLog.traceBegin(SECONDARY_ZYGOTE_PRELOAD); + if (!Process.ZYGOTE_PROCESS.preloadDefault(Build.SUPPORTED_32_BIT_ABIS[0])) { + Slog.e(TAG, "Unable to preload default resources"); + } + traceLog.traceEnd(); + } catch (Exception ex) { + Slog.e(TAG, "Exception preloading default resources", ex); + } + }, SECONDARY_ZYGOTE_PRELOAD); + + t.traceBegin("StartKeyAttestationApplicationIdProviderService"); + ServiceManager.addService("sec_key_att_app_id_provider", + new KeyAttestationApplicationIdProviderService(context)); + t.traceEnd(); + + t.traceBegin("StartKeyChainSystemService"); + mSystemServiceManager.startService(KeyChainSystemService.class); + t.traceEnd(); + + t.traceBegin("StartSchedulingPolicyService"); + ServiceManager.addService("scheduling_policy", new SchedulingPolicyService()); + t.traceEnd(); + + t.traceBegin("StartTelecomLoaderService"); + mSystemServiceManager.startService(TelecomLoaderService.class); + t.traceEnd(); + + t.traceBegin("StartTelephonyRegistry"); + telephonyRegistry = new TelephonyRegistry( + context, new TelephonyRegistry.ConfigurationProvider()); + ServiceManager.addService("telephony.registry", telephonyRegistry); + t.traceEnd(); + + t.traceBegin("StartEntropyMixer"); + mEntropyMixer = new EntropyMixer(context); + t.traceEnd(); + + mContentResolver = context.getContentResolver(); + + // The AccountManager must come before the ContentService + t.traceBegin("StartAccountManagerService"); + mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartContentService"); + mSystemServiceManager.startService(CONTENT_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("InstallSystemProviders"); + mActivityManagerService.getContentProviderHelper().installSystemProviders(); + // Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlags + SQLiteCompatibilityWalFlags.reset(); + t.traceEnd(); + + // Records errors and logs, for example wtf() + // Currently this service indirectly depends on SettingsProvider so do this after + // InstallSystemProviders. + t.traceBegin("StartDropBoxManager"); + mSystemServiceManager.startService(DropBoxManagerService.class); + t.traceEnd(); + + // Grants default permissions and defines roles + t.traceBegin("StartRoleManagerService"); + LocalManagerRegistry.addManager(RoleServicePlatformHelper.class, + new RoleServicePlatformHelperImpl(mSystemContext)); + mSystemServiceManager.startService(ROLE_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartVibratorManagerService"); + mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartDynamicSystemService"); + dynamicSystem = new DynamicSystemService(context); + ServiceManager.addService("dynamic_system", dynamicSystem); + t.traceEnd(); + + if (!isWatch) { + t.traceBegin("StartConsumerIrService"); + consumerIr = new ConsumerIrService(context); + ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr); + t.traceEnd(); + } + + t.traceBegin("StartAlarmManagerService"); + mSystemServiceManager.startService(ALARM_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartInputManagerService"); + inputManager = new InputManagerService(context); + t.traceEnd(); + + t.traceBegin("DeviceStateManagerService"); + mSystemServiceManager.startService(DeviceStateManagerService.class); + t.traceEnd(); + + if (!disableCameraService) { + t.traceBegin("StartCameraServiceProxy"); + mSystemServiceManager.startService(CameraServiceProxy.class); + t.traceEnd(); + } + + t.traceBegin("StartWindowManagerService"); + // WMS needs sensor service ready + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE); + wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, + new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); + ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, + DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); + ServiceManager.addService(Context.INPUT_SERVICE, inputManager, + /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); + t.traceEnd(); + + t.traceBegin("SetWindowManagerService"); + mActivityManagerService.setWindowManager(wm); + t.traceEnd(); + + t.traceBegin("WindowManagerServiceOnInitReady"); + wm.onInitReady(); + t.traceEnd(); + + // Start receiving calls from HIDL services. Start in in a separate thread + // because it need to connect to SensorManager. This has to start + // after PHASE_WAIT_FOR_SENSOR_SERVICE is done. + SystemServerInitThreadPool.submit(() -> { + TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); + traceLog.traceBegin(START_HIDL_SERVICES); + startHidlServices(); + traceLog.traceEnd(); + }, START_HIDL_SERVICES); + + if (!isWatch && enableVrService) { + t.traceBegin("StartVrManagerService"); + mSystemServiceManager.startService(VrManagerService.class); + t.traceEnd(); + } + + t.traceBegin("StartInputManager"); + inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback()); + inputManager.start(); + t.traceEnd(); + + // TODO: Use service dependencies instead. + t.traceBegin("DisplayManagerWindowManagerAndInputReady"); + mDisplayManagerService.windowManagerAndInputReady(); + t.traceEnd(); + + if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) { + Slog.i(TAG, "No Bluetooth Service (factory test)"); + } else if (!context.getPackageManager().hasSystemFeature + (PackageManager.FEATURE_BLUETOOTH)) { + Slog.i(TAG, "No Bluetooth Service (Bluetooth Hardware Not Present)"); + } else { + t.traceBegin("StartBluetoothService"); + mSystemServiceManager.startService(BluetoothService.class); + t.traceEnd(); + } + + t.traceBegin("IpConnectivityMetrics"); + mSystemServiceManager.startService(IP_CONNECTIVITY_METRICS_CLASS); + t.traceEnd(); + + t.traceBegin("NetworkWatchlistService"); + mSystemServiceManager.startService(NetworkWatchlistService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("PinnerService"); + mSystemServiceManager.startService(PinnerService.class); + t.traceEnd(); + + t.traceBegin("IorapForwardingService"); + mSystemServiceManager.startService(IorapForwardingService.class); + t.traceEnd(); + + if (Build.IS_DEBUGGABLE && ProfcollectForwardingService.enabled()) { + t.traceBegin("ProfcollectForwardingService"); + mSystemServiceManager.startService(ProfcollectForwardingService.class); + t.traceEnd(); + } + + t.traceBegin("SignedConfigService"); + SignedConfigService.registerUpdateReceiver(mSystemContext); + t.traceEnd(); + + t.traceBegin("AppIntegrityService"); + mSystemServiceManager.startService(AppIntegrityManagerService.class); + t.traceEnd(); + + } catch (Throwable e) { + Slog.e("System", "******************************************"); + Slog.e("System", "************ Failure starting core service"); + throw e; + } + + // Before things start rolling, be sure we have decided whether + // we are in safe mode. + final boolean safeMode = wm.detectSafeMode(); + if (safeMode) { + // If yes, immediately turn on the global setting for airplane mode. + // Note that this does not send broadcasts at this stage because + // subsystems are not yet up. We will send broadcasts later to ensure + // all listeners have the chance to react with special handling. + Settings.Global.putInt(context.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 1); + } + + StatusBarManagerService statusBar = null; + INotificationManager notification = null; + CountryDetectorService countryDetector = null; + ILockSettings lockSettings = null; + MediaRouterService mediaRouter = null; + + // Bring up services needed for UI. + if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) { + t.traceBegin("StartInputMethodManagerLifecycle"); + if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) { + mSystemServiceManager.startService( + MultiClientInputMethodManagerService.Lifecycle.class); + } else { + mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class); + } + t.traceEnd(); + + t.traceBegin("StartAccessibilityManagerService"); + try { + mSystemServiceManager.startService(ACCESSIBILITY_MANAGER_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting Accessibility Manager", e); + } + t.traceEnd(); + } + + t.traceBegin("MakeDisplayReady"); + try { + wm.displayReady(); + } catch (Throwable e) { + reportWtf("making display ready", e); + } + t.traceEnd(); + + if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) { + if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) { + t.traceBegin("StartStorageManagerService"); + try { + /* + * NotificationManagerService is dependant on StorageManagerService, + * (for media / usb notifications) so we must start StorageManagerService first. + */ + mSystemServiceManager.startService(STORAGE_MANAGER_SERVICE_CLASS); + storageManager = IStorageManager.Stub.asInterface( + ServiceManager.getService("mount")); + } catch (Throwable e) { + reportWtf("starting StorageManagerService", e); + } + t.traceEnd(); + + t.traceBegin("StartStorageStatsService"); + try { + mSystemServiceManager.startService(STORAGE_STATS_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting StorageStatsService", e); + } + t.traceEnd(); + } + } + + // We start this here so that we update our configuration to set watch or television + // as appropriate. + t.traceBegin("StartUiModeManager"); + mSystemServiceManager.startService(UiModeManagerService.class); + t.traceEnd(); + + if (!mOnlyCore) { + t.traceBegin("UpdatePackagesIfNeeded"); + try { + Watchdog.getInstance().pauseWatchingCurrentThread("dexopt"); + mPackageManagerService.updatePackagesIfNeeded(); + } catch (Throwable e) { + reportWtf("update packages", e); + } finally { + Watchdog.getInstance().resumeWatchingCurrentThread("dexopt"); + } + t.traceEnd(); + } + + t.traceBegin("PerformFstrimIfNeeded"); + try { + mPackageManagerService.performFstrimIfNeeded(); + } catch (Throwable e) { + reportWtf("performing fstrim", e); + } + t.traceEnd(); + + final DevicePolicyManagerService.Lifecycle dpms; + if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) { + dpms = null; + } else { + t.traceBegin("StartLockSettingsService"); + try { + mSystemServiceManager.startService(LOCK_SETTINGS_SERVICE_CLASS); + lockSettings = ILockSettings.Stub.asInterface( + ServiceManager.getService("lock_settings")); + } catch (Throwable e) { + reportWtf("starting LockSettingsService service", e); + } + t.traceEnd(); + + final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals(""); + if (hasPdb) { + t.traceBegin("StartPersistentDataBlock"); + mSystemServiceManager.startService(PersistentDataBlockService.class); + t.traceEnd(); + } + + t.traceBegin("StartTestHarnessMode"); + mSystemServiceManager.startService(TestHarnessModeService.class); + t.traceEnd(); + + if (hasPdb || OemLockService.isHalPresent()) { + // Implementation depends on pdb or the OemLock HAL + t.traceBegin("StartOemLockService"); + mSystemServiceManager.startService(OemLockService.class); + t.traceEnd(); + } + + t.traceBegin("StartDeviceIdleController"); + mSystemServiceManager.startService(DEVICE_IDLE_CONTROLLER_CLASS); + t.traceEnd(); + + // Always start the Device Policy Manager, so that the API is compatible with + // API8. + t.traceBegin("StartDevicePolicyManager"); + dpms = mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class); + t.traceEnd(); + + if (!isWatch) { + t.traceBegin("StartStatusBarManagerService"); + try { + statusBar = new StatusBarManagerService(context); + ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar); + } catch (Throwable e) { + reportWtf("starting StatusBarManagerService", e); + } + t.traceEnd(); + } + + if (deviceHasConfigString(context, + R.string.config_defaultMusicRecognitionService)) { + t.traceBegin("StartMusicRecognitionManagerService"); + mSystemServiceManager.startService(MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, + "MusicRecognitionManagerService not defined by OEM or disabled by flag"); + } + + startContentCaptureService(context, t); + startAttentionService(context, t); + startRotationResolverService(context, t); + startSystemCaptionsManagerService(context, t); + startTextToSpeechManagerService(context, t); + + // System Speech Recognition Service + t.traceBegin("StartSpeechRecognitionManagerService"); + mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + // App prediction manager service + if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) { + t.traceBegin("StartAppPredictionService"); + mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "AppPredictionService not defined by OEM"); + } + + // Content suggestions manager service + if (deviceHasConfigString(context, R.string.config_defaultContentSuggestionsService)) { + t.traceBegin("StartContentSuggestionsService"); + mSystemServiceManager.startService(CONTENT_SUGGESTIONS_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "ContentSuggestionsService not defined by OEM"); + } + + // Search UI manager service + // TODO: add deviceHasConfigString(context, R.string.config_defaultSearchUiService) + t.traceBegin("StartSearchUiService"); + mSystemServiceManager.startService(SEARCH_UI_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + // Smartspace manager service + // TODO: add deviceHasConfigString(context, R.string.config_defaultSmartspaceService) + t.traceBegin("StartSmartspaceService"); + mSystemServiceManager.startService(SMARTSPACE_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("InitConnectivityModuleConnector"); + try { + ConnectivityModuleConnector.getInstance().init(context); + } catch (Throwable e) { + reportWtf("initializing ConnectivityModuleConnector", e); + } + t.traceEnd(); + + t.traceBegin("InitNetworkStackClient"); + try { + NetworkStackClient.getInstance().init(); + } catch (Throwable e) { + reportWtf("initializing NetworkStackClient", e); + } + t.traceEnd(); + + t.traceBegin("StartNetworkManagementService"); + try { + networkManagement = NetworkManagementService.create(context); + ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement); + } catch (Throwable e) { + reportWtf("starting NetworkManagement Service", e); + } + t.traceEnd(); + + + t.traceBegin("StartIpSecService"); + try { + ipSecService = IpSecService.create(context); + ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService); + } catch (Throwable e) { + reportWtf("starting IpSec Service", e); + } + t.traceEnd(); + + t.traceBegin("StartFontManagerService"); + mSystemServiceManager.startService(new FontManagerService.Lifecycle(context, safeMode)); + t.traceEnd(); + + t.traceBegin("StartTextServicesManager"); + mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); + t.traceEnd(); + + if (!disableSystemTextClassifier) { + t.traceBegin("StartTextClassificationManagerService"); + mSystemServiceManager + .startService(TextClassificationManagerService.Lifecycle.class); + t.traceEnd(); + } + + t.traceBegin("StartNetworkScoreService"); + mSystemServiceManager.startService(NetworkScoreService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartNetworkStatsService"); + try { + networkStats = NetworkStatsService.create(context, networkManagement); + ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats); + } catch (Throwable e) { + reportWtf("starting NetworkStats Service", e); + } + t.traceEnd(); + + t.traceBegin("StartMikRomManagerService"); + try { + ServiceManager.addService(Context.MIKROM_SERVICE,new MikRomManagerService()); + } catch (Throwable e) { + reportWtf("starting MikRom Service", e); + } + t.traceEnd(); + + t.traceBegin("StartNetworkPolicyManagerService"); + try { + networkPolicy = new NetworkPolicyManagerService(context, mActivityManagerService, + networkManagement); + ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy); + } catch (Throwable e) { + reportWtf("starting NetworkPolicy Service", e); + } + t.traceEnd(); + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI)) { + // Wifi Service must be started first for wifi-related services. + t.traceBegin("StartWifi"); + mSystemServiceManager.startServiceFromJar( + WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + t.traceBegin("StartWifiScanning"); + mSystemServiceManager.startServiceFromJar( + WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_RTT)) { + t.traceBegin("StartRttService"); + mSystemServiceManager.startServiceFromJar( + WIFI_RTT_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_AWARE)) { + t.traceBegin("StartWifiAware"); + mSystemServiceManager.startServiceFromJar( + WIFI_AWARE_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_DIRECT)) { + t.traceBegin("StartWifiP2P"); + mSystemServiceManager.startServiceFromJar( + WIFI_P2P_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_LOWPAN)) { + t.traceBegin("StartLowpan"); + mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) || + mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) { + t.traceBegin("StartEthernet"); + mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartPacProxyService"); + try { + pacProxyService = new PacProxyService(context); + ServiceManager.addService(Context.PAC_PROXY_SERVICE, pacProxyService); + } catch (Throwable e) { + reportWtf("starting PacProxyService", e); + } + t.traceEnd(); + + t.traceBegin("StartConnectivityService"); + // This has to be called after NetworkManagementService, NetworkStatsService + // and NetworkPolicyManager because ConnectivityService needs to take these + // services to initialize. + mSystemServiceManager.startServiceFromJar(CONNECTIVITY_SERVICE_INITIALIZER_CLASS, + CONNECTIVITY_SERVICE_APEX_PATH); + networkPolicy.bindConnectivityManager(); + t.traceEnd(); + + t.traceBegin("StartVpnManagerService"); + try { + vpnManager = VpnManagerService.create(context); + ServiceManager.addService(Context.VPN_MANAGEMENT_SERVICE, vpnManager); + } catch (Throwable e) { + reportWtf("starting VPN Manager Service", e); + } + t.traceEnd(); + + t.traceBegin("StartVcnManagementService"); + try { + vcnManagement = VcnManagementService.create(context); + ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement); + } catch (Throwable e) { + reportWtf("starting VCN Management Service", e); + } + t.traceEnd(); + + t.traceBegin("StartNsdService"); + try { + serviceDiscovery = NsdService.create(context); + ServiceManager.addService( + Context.NSD_SERVICE, serviceDiscovery); + } catch (Throwable e) { + reportWtf("starting Service Discovery Service", e); + } + t.traceEnd(); + + t.traceBegin("StartSystemUpdateManagerService"); + try { + ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE, + new SystemUpdateManagerService(context)); + } catch (Throwable e) { + reportWtf("starting SystemUpdateManagerService", e); + } + t.traceEnd(); + + t.traceBegin("StartUpdateLockService"); + try { + ServiceManager.addService(Context.UPDATE_LOCK_SERVICE, + new UpdateLockService(context)); + } catch (Throwable e) { + reportWtf("starting UpdateLockService", e); + } + t.traceEnd(); + + t.traceBegin("StartNotificationManager"); + mSystemServiceManager.startService(NotificationManagerService.class); + SystemNotificationChannels.removeDeprecated(context); + SystemNotificationChannels.createAll(context); + notification = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + t.traceEnd(); + + t.traceBegin("StartDeviceMonitor"); + mSystemServiceManager.startService(DeviceStorageMonitorService.class); + t.traceEnd(); + + t.traceBegin("StartLocationManagerService"); + mSystemServiceManager.startService(LocationManagerService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartCountryDetectorService"); + try { + countryDetector = new CountryDetectorService(context); + ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector); + } catch (Throwable e) { + reportWtf("starting Country Detector", e); + } + t.traceEnd(); + + t.traceBegin("StartTimeDetectorService"); + try { + mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting TimeDetectorService service", e); + } + t.traceEnd(); + + t.traceBegin("StartTimeZoneDetectorService"); + try { + mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting TimeZoneDetectorService service", e); + } + t.traceEnd(); + + t.traceBegin("StartLocationTimeZoneManagerService"); + try { + mSystemServiceManager.startService(LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting LocationTimeZoneManagerService service", e); + } + t.traceEnd(); + + if (context.getResources().getBoolean(R.bool.config_enableGnssTimeUpdateService)) { + t.traceBegin("StartGnssTimeUpdateService"); + try { + mSystemServiceManager.startService(GNSS_TIME_UPDATE_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting GnssTimeUpdateService service", e); + } + t.traceEnd(); + } + + if (!isWatch) { + t.traceBegin("StartSearchManagerService"); + try { + mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting Search Service", e); + } + t.traceEnd(); + } + + if (context.getResources().getBoolean(R.bool.config_enableWallpaperService)) { + t.traceBegin("StartWallpaperManagerService"); + mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.i(TAG, "Wallpaper service disabled by config"); + } + + t.traceBegin("StartAudioService"); + if (!isArc) { + mSystemServiceManager.startService(AudioService.Lifecycle.class); + } else { + String className = context.getResources() + .getString(R.string.config_deviceSpecificAudioService); + try { + mSystemServiceManager.startService(className + "$Lifecycle"); + } catch (Throwable e) { + reportWtf("starting " + className, e); + } + } + t.traceEnd(); + + t.traceBegin("StartSoundTriggerMiddlewareService"); + mSystemServiceManager.startService(SoundTriggerMiddlewareService.Lifecycle.class); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BROADCAST_RADIO)) { + t.traceBegin("StartBroadcastRadioService"); + mSystemServiceManager.startService(BroadcastRadioService.class); + t.traceEnd(); + } + + t.traceBegin("StartDockObserver"); + mSystemServiceManager.startService(DockObserver.class); + t.traceEnd(); + + if (isWatch) { + t.traceBegin("StartThermalObserver"); + mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartWiredAccessoryManager"); + try { + // Listen for wired headset changes + inputManager.setWiredAccessoryCallbacks( + new WiredAccessoryManager(context, inputManager)); + } catch (Throwable e) { + reportWtf("starting WiredAccessoryManager", e); + } + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) { + // Start MIDI Manager service + t.traceBegin("StartMidiManager"); + mSystemServiceManager.startService(MIDI_SERVICE_CLASS); + t.traceEnd(); + } + + // Start ADB Debugging Service + t.traceBegin("StartAdbService"); + try { + mSystemServiceManager.startService(ADB_SERVICE_CLASS); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting AdbService"); + } + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST) + || mPackageManager.hasSystemFeature( + PackageManager.FEATURE_USB_ACCESSORY) + || isEmulator) { + // Manage USB host and device support + t.traceBegin("StartUsbService"); + mSystemServiceManager.startService(USB_SERVICE_CLASS); + t.traceEnd(); + } + + if (!isWatch) { + t.traceBegin("StartSerialService"); + try { + // Serial port support + serial = new SerialService(context); + ServiceManager.addService(Context.SERIAL_SERVICE, serial); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting SerialService", e); + } + t.traceEnd(); + } + + t.traceBegin("StartHardwarePropertiesManagerService"); + try { + hardwarePropertiesService = new HardwarePropertiesManagerService(context); + ServiceManager.addService(Context.HARDWARE_PROPERTIES_SERVICE, + hardwarePropertiesService); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting HardwarePropertiesManagerService", e); + } + t.traceEnd(); + + t.traceBegin("StartTwilightService"); + mSystemServiceManager.startService(TwilightService.class); + t.traceEnd(); + + t.traceBegin("StartColorDisplay"); + mSystemServiceManager.startService(ColorDisplayService.class); + t.traceEnd(); + + // TODO(aml-jobscheduler): Think about how to do it properly. + t.traceBegin("StartJobScheduler"); + mSystemServiceManager.startService(JOB_SCHEDULER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartSoundTrigger"); + mSystemServiceManager.startService(SoundTriggerService.class); + t.traceEnd(); + + t.traceBegin("StartTrustManager"); + mSystemServiceManager.startService(TrustManagerService.class); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) { + t.traceBegin("StartBackupManager"); + mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS) + || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) { + t.traceBegin("StartAppWidgetService"); + mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS); + t.traceEnd(); + } + + // We need to always start this service, regardless of whether the + // FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care + // of initializing various settings. It will internally modify its behavior + // based on that feature. + t.traceBegin("StartVoiceRecognitionManager"); + mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartAppHibernationService"); + mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS); + t.traceEnd(); + + if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) { + t.traceBegin("StartGestureLauncher"); + mSystemServiceManager.startService(GestureLauncherService.class); + t.traceEnd(); + } + t.traceBegin("StartSensorNotification"); + mSystemServiceManager.startService(SensorNotificationService.class); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CONTEXT_HUB)) { + t.traceBegin("StartContextHubSystemService"); + mSystemServiceManager.startService(ContextHubSystemService.class); + t.traceEnd(); + } + + t.traceBegin("StartDiskStatsService"); + try { + ServiceManager.addService("diskstats", new DiskStatsService(context)); + } catch (Throwable e) { + reportWtf("starting DiskStats Service", e); + } + t.traceEnd(); + + t.traceBegin("RuntimeService"); + try { + ServiceManager.addService("runtime", new RuntimeService(context)); + } catch (Throwable e) { + reportWtf("starting RuntimeService", e); + } + t.traceEnd(); + + // timezone.RulesManagerService will prevent a device starting up if the chain of trust + // required for safe time zone updates might be broken. RuleManagerService cannot do + // this check when mOnlyCore == true, so we don't enable the service in this case. + // This service requires that JobSchedulerService is already started when it starts. + final boolean startRulesManagerService = + !mOnlyCore && context.getResources().getBoolean( + R.bool.config_enableUpdateableTimeZoneRules); + if (startRulesManagerService) { + t.traceBegin("StartTimeZoneRulesManagerService"); + mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + if (!isWatch && !disableNetworkTime) { + t.traceBegin("StartNetworkTimeUpdateService"); + try { + networkTimeUpdater = new NetworkTimeUpdateService(context); + ServiceManager.addService("network_time_update_service", networkTimeUpdater); + } catch (Throwable e) { + reportWtf("starting NetworkTimeUpdate service", e); + } + t.traceEnd(); + } + + t.traceBegin("CertBlacklister"); + try { + CertBlacklister blacklister = new CertBlacklister(context); + } catch (Throwable e) { + reportWtf("starting CertBlacklister", e); + } + t.traceEnd(); + + if (EmergencyAffordanceManager.ENABLED) { + // EmergencyMode service + t.traceBegin("StartEmergencyAffordanceService"); + mSystemServiceManager.startService(EmergencyAffordanceService.class); + t.traceEnd(); + } + + mBlobStoreServiceStart = SystemServerInitThreadPool.submit(() -> { + final TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); + traceLog.traceBegin(START_BLOB_STORE_SERVICE); + mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS); + traceLog.traceEnd(); + }, START_BLOB_STORE_SERVICE); + + // Dreams (interactive idle-time views, a/k/a screen savers, and doze mode) + t.traceBegin("StartDreamManager"); + mSystemServiceManager.startService(DreamManagerService.class); + t.traceEnd(); + + t.traceBegin("AddGraphicsStatsService"); + ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE, + new GraphicsStatsService(context)); + t.traceEnd(); + + if (CoverageService.ENABLED) { + t.traceBegin("AddCoverageService"); + ServiceManager.addService(CoverageService.COVERAGE_SERVICE, new CoverageService()); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) { + t.traceBegin("StartPrintManager"); + mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) { + t.traceBegin("StartCompanionDeviceManager"); + mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartRestrictionManager"); + mSystemServiceManager.startService(RestrictionsManagerService.class); + t.traceEnd(); + + t.traceBegin("StartMediaSessionService"); + mSystemServiceManager.startService(MEDIA_SESSION_SERVICE_CLASS); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) { + t.traceBegin("StartHdmiControlService"); + mSystemServiceManager.startService(HdmiControlService.class); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + t.traceBegin("StartTvInputManager"); + mSystemServiceManager.startService(TvInputManagerService.class); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TUNER)) { + t.traceBegin("StartTunerResourceManager"); + mSystemServiceManager.startService(TunerResourceManagerService.class); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) { + t.traceBegin("StartMediaResourceMonitor"); + mSystemServiceManager.startService(MEDIA_RESOURCE_MONITOR_SERVICE_CLASS); + t.traceEnd(); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + t.traceBegin("StartTvRemoteService"); + mSystemServiceManager.startService(TvRemoteService.class); + t.traceEnd(); + } + + t.traceBegin("StartMediaRouterService"); + try { + mediaRouter = new MediaRouterService(context); + ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, mediaRouter); + } catch (Throwable e) { + reportWtf("starting MediaRouterService", e); + } + t.traceEnd(); + + final boolean hasFeatureFace + = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE); + final boolean hasFeatureIris + = mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS); + final boolean hasFeatureFingerprint + = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); + + if (hasFeatureFace) { + t.traceBegin("StartFaceSensor"); + final FaceService faceService = + mSystemServiceManager.startService(FaceService.class); + t.traceEnd(); + } + + if (hasFeatureIris) { + t.traceBegin("StartIrisSensor"); + mSystemServiceManager.startService(IrisService.class); + t.traceEnd(); + } + + if (hasFeatureFingerprint) { + t.traceBegin("StartFingerprintSensor"); + final FingerprintService fingerprintService = + mSystemServiceManager.startService(FingerprintService.class); + t.traceEnd(); + } + + // Start this service after all biometric sensor services are started. + t.traceBegin("StartBiometricService"); + mSystemServiceManager.startService(BiometricService.class); + t.traceEnd(); + + t.traceBegin("StartAuthService"); + mSystemServiceManager.startService(AuthService.class); + t.traceEnd(); + + + t.traceBegin("StartBackgroundDexOptService"); + try { + BackgroundDexOptService.schedule(context); + } catch (Throwable e) { + reportWtf("starting StartBackgroundDexOptService", e); + } + t.traceEnd(); + + if (!isWatch) { + // We don't run this on watches as there are no plans to use the data logged + // on watch devices. + t.traceBegin("StartDynamicCodeLoggingService"); + try { + DynamicCodeLoggingService.schedule(context); + } catch (Throwable e) { + reportWtf("starting DynamicCodeLoggingService", e); + } + t.traceEnd(); + } + + if (!isWatch) { + t.traceBegin("StartPruneInstantAppsJobService"); + try { + PruneInstantAppsJobService.schedule(context); + } catch (Throwable e) { + reportWtf("StartPruneInstantAppsJobService", e); + } + t.traceEnd(); + } + + // LauncherAppsService uses ShortcutService. + t.traceBegin("StartShortcutServiceLifecycle"); + mSystemServiceManager.startService(ShortcutService.Lifecycle.class); + t.traceEnd(); + + t.traceBegin("StartLauncherAppsService"); + mSystemServiceManager.startService(LauncherAppsService.class); + t.traceEnd(); + + t.traceBegin("StartCrossProfileAppsService"); + mSystemServiceManager.startService(CrossProfileAppsService.class); + t.traceEnd(); + + t.traceBegin("StartPeopleService"); + mSystemServiceManager.startService(PeopleService.class); + t.traceEnd(); + + t.traceBegin("StartMediaMetricsManager"); + mSystemServiceManager.startService(MediaMetricsManagerService.class); + t.traceEnd(); + } + + if (!isWatch) { + t.traceBegin("StartMediaProjectionManager"); + mSystemServiceManager.startService(MediaProjectionManagerService.class); + t.traceEnd(); + } + + if (isWatch) { + // Must be started before services that depend it, e.g. WearConnectivityService + t.traceBegin("StartWearPowerService"); + mSystemServiceManager.startService(WEAR_POWER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartWearConnectivityService"); + mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartWearDisplayService"); + mSystemServiceManager.startService(WEAR_DISPLAY_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartWearTimeService"); + mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS); + t.traceEnd(); + + if (enableLeftyService) { + t.traceBegin("StartWearLeftyService"); + mSystemServiceManager.startService(WEAR_LEFTY_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartWearGlobalActionsService"); + mSystemServiceManager.startService(WEAR_GLOBAL_ACTIONS_SERVICE_CLASS); + t.traceEnd(); + } + + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) { + t.traceBegin("StartSliceManagerService"); + mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) { + t.traceBegin("StartIoTSystemService"); + mSystemServiceManager.startService(IOT_SERVICE_CLASS); + t.traceEnd(); + } + + // Statsd helper + t.traceBegin("StartStatsCompanion"); + mSystemServiceManager.startServiceFromJar( + STATS_COMPANION_LIFECYCLE_CLASS, STATS_COMPANION_APEX_PATH); + t.traceEnd(); + + // Reboot Readiness + t.traceBegin("StartRebootReadinessManagerService"); + mSystemServiceManager.startServiceFromJar( + REBOOT_READINESS_LIFECYCLE_CLASS, SCHEDULING_APEX_PATH); + t.traceEnd(); + + // Statsd pulled atoms + t.traceBegin("StartStatsPullAtomService"); + mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS); + t.traceEnd(); + + // Incidentd and dumpstated helper + t.traceBegin("StartIncidentCompanionService"); + mSystemServiceManager.startService(IncidentCompanionService.class); + t.traceEnd(); + + if (safeMode) { + mActivityManagerService.enterSafeMode(); + } + + // MMS service broker + t.traceBegin("StartMmsService"); + mmsService = mSystemServiceManager.startService(MmsServiceBroker.class); + t.traceEnd(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOFILL)) { + t.traceBegin("StartAutoFillService"); + mSystemServiceManager.startService(AUTO_FILL_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + // Translation manager service + if (deviceHasConfigString(context, R.string.config_defaultTranslationService)) { + t.traceBegin("StartTranslationManagerService"); + mSystemServiceManager.startService(TRANSLATION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "TranslationService not defined by OEM"); + } + + // NOTE: ClipboardService depends on ContentCapture and Autofill + t.traceBegin("StartClipboardService"); + mSystemServiceManager.startService(ClipboardService.class); + t.traceEnd(); + + t.traceBegin("AppServiceManager"); + mSystemServiceManager.startService(AppBindingService.Lifecycle.class); + t.traceEnd(); + + // Perfetto TracingServiceProxy + t.traceBegin("startTracingServiceProxy"); + mSystemServiceManager.startService(TracingServiceProxy.class); + t.traceEnd(); + + // It is now time to start up the app processes... + + t.traceBegin("MakeLockSettingsServiceReady"); + if (lockSettings != null) { + try { + lockSettings.systemReady(); + } catch (Throwable e) { + reportWtf("making Lock Settings Service ready", e); + } + } + t.traceEnd(); + + // Needed by DevicePolicyManager for initialization + t.traceBegin("StartBootPhaseLockSettingsReady"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_LOCK_SETTINGS_READY); + t.traceEnd(); + + t.traceBegin("StartBootPhaseSystemServicesReady"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_SYSTEM_SERVICES_READY); + t.traceEnd(); + + t.traceBegin("MakeWindowManagerServiceReady"); + try { + wm.systemReady(); + } catch (Throwable e) { + reportWtf("making Window Manager Service ready", e); + } + t.traceEnd(); + + // Emit any pending system_server WTFs + synchronized (SystemService.class) { + if (sPendingWtfs != null) { + mActivityManagerService.schedulePendingSystemServerWtfs(sPendingWtfs); + sPendingWtfs = null; + } + } + + if (safeMode) { + mActivityManagerService.showSafeModeOverlay(); + } + + // Update the configuration for this context by hand, because we're going + // to start using it before the config change done in wm.systemReady() will + // propagate to it. + final Configuration config = wm.computeNewConfiguration(DEFAULT_DISPLAY); + DisplayMetrics metrics = new DisplayMetrics(); + context.getDisplay().getMetrics(metrics); + context.getResources().updateConfiguration(config, metrics); + + // The system context's theme may be configuration-dependent. + final Theme systemTheme = context.getTheme(); + if (systemTheme.getChangingConfigurations() != 0) { + systemTheme.rebase(); + } + + t.traceBegin("MakePowerManagerServiceReady"); + try { + // TODO: use boot phase + mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService()); + } catch (Throwable e) { + reportWtf("making Power Manager Service ready", e); + } + t.traceEnd(); + + // Permission policy service + t.traceBegin("StartPermissionPolicyService"); + mSystemServiceManager.startService(PermissionPolicyService.class); + t.traceEnd(); + + t.traceBegin("MakePackageManagerServiceReady"); + mPackageManagerService.systemReady(); + t.traceEnd(); + + t.traceBegin("MakeDisplayManagerServiceReady"); + try { + // TODO: use boot phase and communicate these flags some other way + mDisplayManagerService.systemReady(safeMode, mOnlyCore); + } catch (Throwable e) { + reportWtf("making Display Manager Service ready", e); + } + t.traceEnd(); + + mSystemServiceManager.setSafeMode(safeMode); + + // Start device specific services + t.traceBegin("StartDeviceSpecificServices"); + final String[] classes = mSystemContext.getResources().getStringArray( + R.array.config_deviceSpecificSystemServices); + for (final String className : classes) { + t.traceBegin("StartDeviceSpecificServices " + className); + try { + mSystemServiceManager.startService(className); + } catch (Throwable e) { + reportWtf("starting " + className, e); + } + t.traceEnd(); + } + t.traceEnd(); + + t.traceBegin("GameManagerService"); + mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) { + t.traceBegin("UwbService"); + mSystemServiceManager.startService(UWB_SERVICE_CLASS); + t.traceEnd(); + } + + t.traceBegin("StartBootPhaseDeviceSpecificServicesReady"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY); + t.traceEnd(); + + t.traceBegin("AppSearchManagerService"); + mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartMediaCommunicationService"); + mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS); + t.traceEnd(); + + ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart, + START_BLOB_STORE_SERVICE); + + // These are needed to propagate to the runnable below. + final NetworkManagementService networkManagementF = networkManagement; + final NetworkStatsService networkStatsF = networkStats; + final NetworkPolicyManagerService networkPolicyF = networkPolicy; + final CountryDetectorService countryDetectorF = countryDetector; + final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; + final InputManagerService inputManagerF = inputManager; + final TelephonyRegistry telephonyRegistryF = telephonyRegistry; + final MediaRouterService mediaRouterF = mediaRouter; + final MmsServiceBroker mmsServiceF = mmsService; + final IpSecService ipSecServiceF = ipSecService; + final VpnManagerService vpnManagerF = vpnManager; + final VcnManagementService vcnManagementF = vcnManagement; + final WindowManagerService windowManagerF = wm; + final ConnectivityManager connectivityF = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + + // We now tell the activity manager it is okay to run third party + // code. It will call back into us once it has gotten to the state + // where third party code can really run (but before it has actually + // started launching the initial applications), for us to complete our + // initialization. + mActivityManagerService.systemReady(() -> { + Slog.i(TAG, "Making services ready"); + t.traceBegin("StartActivityManagerReadyPhase"); + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_ACTIVITY_MANAGER_READY); + t.traceEnd(); + t.traceBegin("StartObservingNativeCrashes"); + try { + mActivityManagerService.startObservingNativeCrashes(); + } catch (Throwable e) { + reportWtf("observing native crashes", e); + } + t.traceEnd(); + + t.traceBegin("RegisterAppOpsPolicy"); + try { + mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy(mSystemContext)); + } catch (Throwable e) { + reportWtf("registering app ops policy", e); + } + t.traceEnd(); + + // No dependency on Webview preparation in system server. But this should + // be completed before allowing 3rd party + final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation"; + Future webviewPrep = null; + if (!mOnlyCore && mWebViewUpdateService != null) { + webviewPrep = SystemServerInitThreadPool.submit(() -> { + Slog.i(TAG, WEBVIEW_PREPARATION); + TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog(); + traceLog.traceBegin(WEBVIEW_PREPARATION); + ConcurrentUtils.waitForFutureNoInterrupt(mZygotePreload, "Zygote preload"); + mZygotePreload = null; + mWebViewUpdateService.prepareWebViewInSystemServer(); + traceLog.traceEnd(); + }, WEBVIEW_PREPARATION); + } + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + t.traceBegin("StartCarServiceHelperService"); + final SystemService cshs = mSystemServiceManager + .startService(CAR_SERVICE_HELPER_SERVICE_CLASS); + if (cshs instanceof Dumpable) { + mDumper.addDumpable((Dumpable) cshs); + } + if (cshs instanceof DevicePolicySafetyChecker) { + dpms.setDevicePolicySafetyChecker((DevicePolicySafetyChecker) cshs); + } + t.traceEnd(); + } + + // Enable airplane mode in safe mode. setAirplaneMode() cannot be called + // earlier as it sends broadcasts to other services. + // TODO: This may actually be too late if radio firmware already started leaking + // RF before the respective services start. However, fixing this requires changes + // to radio firmware and interfaces. + if (safeMode) { + t.traceBegin("EnableAirplaneModeInSafeMode"); + try { + connectivityF.setAirplaneMode(true); + } catch (Throwable e) { + reportWtf("enabling Airplane Mode during Safe Mode bootup", e); + } + t.traceEnd(); + } + t.traceBegin("MakeNetworkManagementServiceReady"); + try { + if (networkManagementF != null) { + networkManagementF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making Network Managment Service ready", e); + } + CountDownLatch networkPolicyInitReadySignal = null; + if (networkPolicyF != null) { + networkPolicyInitReadySignal = networkPolicyF + .networkScoreAndNetworkManagementServiceReady(); + } + t.traceEnd(); + t.traceBegin("MakeIpSecServiceReady"); + try { + if (ipSecServiceF != null) { + ipSecServiceF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making IpSec Service ready", e); + } + t.traceEnd(); + t.traceBegin("MakeNetworkStatsServiceReady"); + try { + if (networkStatsF != null) { + networkStatsF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making Network Stats Service ready", e); + } + t.traceEnd(); + t.traceBegin("MakeConnectivityServiceReady"); + try { + if (connectivityF != null) { + connectivityF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making Connectivity Service ready", e); + } + t.traceEnd(); + t.traceBegin("MakeVpnManagerServiceReady"); + try { + if (vpnManagerF != null) { + vpnManagerF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making VpnManagerService ready", e); + } + t.traceEnd(); + t.traceBegin("MakeVcnManagementServiceReady"); + try { + if (vcnManagementF != null) { + vcnManagementF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making VcnManagementService ready", e); + } + t.traceEnd(); + t.traceBegin("MakeNetworkPolicyServiceReady"); + try { + if (networkPolicyF != null) { + networkPolicyF.systemReady(networkPolicyInitReadySignal); + } + } catch (Throwable e) { + reportWtf("making Network Policy Service ready", e); + } + t.traceEnd(); + + // Wait for all packages to be prepared + mPackageManagerService.waitForAppDataPrepared(); + + // It is now okay to let the various system services start their + // third party code... + t.traceBegin("PhaseThirdPartyAppsCanStart"); + // confirm webview completion before starting 3rd party + if (webviewPrep != null) { + ConcurrentUtils.waitForFutureNoInterrupt(webviewPrep, WEBVIEW_PREPARATION); + } + mSystemServiceManager.startBootPhase(t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START); + t.traceEnd(); + + t.traceBegin("StartNetworkStack"); + try { + // Note : the network stack is creating on-demand objects that need to send + // broadcasts, which means it currently depends on being started after + // ActivityManagerService.mSystemReady and ActivityManagerService.mProcessesReady + // are set to true. Be careful if moving this to a different place in the + // startup sequence. + NetworkStackClient.getInstance().start(); + } catch (Throwable e) { + reportWtf("starting Network Stack", e); + } + t.traceEnd(); + + t.traceBegin("StartTethering"); + try { + // TODO: hide implementation details, b/146312721. + ConnectivityModuleConnector.getInstance().startModuleService( + TETHERING_CONNECTOR_CLASS, + PERMISSION_MAINLINE_NETWORK_STACK, service -> { + ServiceManager.addService(Context.TETHERING_SERVICE, service, + false /* allowIsolated */, + DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); + }); + } catch (Throwable e) { + reportWtf("starting Tethering", e); + } + t.traceEnd(); + + t.traceBegin("MakeCountryDetectionServiceReady"); + try { + if (countryDetectorF != null) { + countryDetectorF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying CountryDetectorService running", e); + } + t.traceEnd(); + t.traceBegin("MakeNetworkTimeUpdateReady"); + try { + if (networkTimeUpdaterF != null) { + networkTimeUpdaterF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying NetworkTimeService running", e); + } + t.traceEnd(); + t.traceBegin("MakeInputManagerServiceReady"); + try { + // TODO(BT) Pass parameter to input manager + if (inputManagerF != null) { + inputManagerF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying InputManagerService running", e); + } + t.traceEnd(); + t.traceBegin("MakeTelephonyRegistryReady"); + try { + if (telephonyRegistryF != null) { + telephonyRegistryF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying TelephonyRegistry running", e); + } + t.traceEnd(); + t.traceBegin("MakeMediaRouterServiceReady"); + try { + if (mediaRouterF != null) { + mediaRouterF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying MediaRouterService running", e); + } + t.traceEnd(); + t.traceBegin("MakeMmsServiceReady"); + try { + if (mmsServiceF != null) { + mmsServiceF.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying MmsService running", e); + } + t.traceEnd(); + + t.traceBegin("IncidentDaemonReady"); + try { + // TODO: Switch from checkService to getService once it's always + // in the build and should reliably be there. + final IIncidentManager incident = IIncidentManager.Stub.asInterface( + ServiceManager.getService(Context.INCIDENT_SERVICE)); + if (incident != null) { + incident.systemRunning(); + } + } catch (Throwable e) { + reportWtf("Notifying incident daemon running", e); + } + t.traceEnd(); + + if (mIncrementalServiceHandle != 0) { + t.traceBegin("MakeIncrementalServiceReady"); + setIncrementalServiceSystemReady(mIncrementalServiceHandle); + t.traceEnd(); + } + }, t); + + t.traceBegin("StartSystemUI"); + try { + startSystemUi(context, windowManagerF); + } catch (Throwable e) { + reportWtf("starting System UI", e); + } + t.traceEnd(); + + t.traceEnd(); // startOtherServices + } + + private boolean deviceHasConfigString(@NonNull Context context, @StringRes int resId) { + String serviceName = context.getString(resId); + return !TextUtils.isEmpty(serviceName); + } + + private void startSystemCaptionsManagerService(@NonNull Context context, + @NonNull TimingsTraceAndSlog t) { + if (!deviceHasConfigString(context, R.string.config_defaultSystemCaptionsManagerService)) { + Slog.d(TAG, "SystemCaptionsManagerService disabled because resource is not overlaid"); + return; + } + + t.traceBegin("StartSystemCaptionsManagerService"); + mSystemServiceManager.startService(SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + private void startTextToSpeechManagerService(@NonNull Context context, + @NonNull TimingsTraceAndSlog t) { + t.traceBegin("StartTextToSpeechManagerService"); + mSystemServiceManager.startService(TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + + private void startContentCaptureService(@NonNull Context context, + @NonNull TimingsTraceAndSlog t) { + // First check if it was explicitly enabled by DeviceConfig + boolean explicitlyEnabled = false; + String settings = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); + if (settings != null && !settings.equalsIgnoreCase("default")) { + explicitlyEnabled = Boolean.parseBoolean(settings); + if (explicitlyEnabled) { + Slog.d(TAG, "ContentCaptureService explicitly enabled by DeviceConfig"); + } else { + Slog.d(TAG, "ContentCaptureService explicitly disabled by DeviceConfig"); + return; + } + } + + // Then check if OEM overlaid the resource that defines the service. + if (!explicitlyEnabled) { + if (!deviceHasConfigString(context, R.string.config_defaultContentCaptureService)) { + Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid"); + return; + } + } + + t.traceBegin("StartContentCaptureService"); + mSystemServiceManager.startService(CONTENT_CAPTURE_MANAGER_SERVICE_CLASS); + + ContentCaptureManagerInternal ccmi = + LocalServices.getService(ContentCaptureManagerInternal.class); + if (ccmi != null && mActivityManagerService != null) { + mActivityManagerService.setContentCaptureManager(ccmi); + } + + t.traceEnd(); + } + + private void startAttentionService(@NonNull Context context, @NonNull TimingsTraceAndSlog t) { + if (!AttentionManagerService.isServiceConfigured(context)) { + Slog.d(TAG, "AttentionService is not configured on this device"); + return; + } + + t.traceBegin("StartAttentionManagerService"); + mSystemServiceManager.startService(AttentionManagerService.class); + t.traceEnd(); + } + + private void startRotationResolverService(@NonNull Context context, + @NonNull TimingsTraceAndSlog t) { + if (!RotationResolverManagerService.isServiceConfigured(context)) { + Slog.d(TAG, "RotationResolverService is not configured on this device"); + return; + } + + t.traceBegin("StartRotationResolverService"); + mSystemServiceManager.startService(RotationResolverManagerService.class); + t.traceEnd(); + + } + + private static void startSystemUi(Context context, WindowManagerService windowManager) { + PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); + Intent intent = new Intent(); + intent.setComponent(pm.getSystemUiServiceComponent()); + intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); + //Slog.d(TAG, "Starting service: " + intent); + context.startServiceAsUser(intent, UserHandle.SYSTEM); + windowManager.onSystemUiStarted(); + } + + /** + * Handle the serious errors during early system boot, used by {@link Log} via + * {@link com.android.internal.os.RuntimeInit}. + */ + private static boolean handleEarlySystemWtf(final IBinder app, final String tag, boolean system, + final ApplicationErrorReport.ParcelableCrashInfo crashInfo, int immediateCallerPid) { + final String processName = "system_server"; + final int myPid = myPid(); + + com.android.server.am.EventLogTags.writeAmWtf(UserHandle.getUserId(SYSTEM_UID), myPid, + processName, -1, tag, crashInfo.exceptionMessage); + + FrameworkStatsLog.write(FrameworkStatsLog.WTF_OCCURRED, SYSTEM_UID, tag, processName, + myPid, ServerProtoEnums.SYSTEM_SERVER); + + synchronized (SystemServer.class) { + if (sPendingWtfs == null) { + sPendingWtfs = new LinkedList<>(); + } + sPendingWtfs.add(new Pair<>(tag, crashInfo)); + } + return false; + } + +} diff --git a/code/chapter-06/mikrom_service/SystemServiceRegistry.java b/code/chapter-06/mikrom_service/SystemServiceRegistry.java new file mode 100644 index 0000000..826c25d --- /dev/null +++ b/code/chapter-06/mikrom_service/SystemServiceRegistry.java @@ -0,0 +1,1969 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.accounts.AccountManager; +import android.accounts.IAccountManager; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.ContextImpl.ServiceInitializationState; +import android.app.admin.DevicePolicyManager; +import android.app.admin.IDevicePolicyManager; +import android.app.appsearch.AppSearchManagerFrameworkInitializer; +import android.app.blob.BlobStoreManagerFrameworkInitializer; +import android.app.contentsuggestions.ContentSuggestionsManager; +import android.app.contentsuggestions.IContentSuggestionsManager; +import android.app.job.JobSchedulerFrameworkInitializer; +import android.app.people.PeopleManager; +import android.app.prediction.AppPredictionManager; +import android.app.role.RoleFrameworkInitializer; +import android.app.search.SearchUiManager; +import android.app.slice.SliceManager; +import android.app.smartspace.SmartspaceManager; +import android.app.time.TimeManager; +import android.app.timedetector.TimeDetector; +import android.app.timedetector.TimeDetectorImpl; +import android.app.timezone.RulesManager; +import android.app.timezonedetector.TimeZoneDetector; +import android.app.timezonedetector.TimeZoneDetectorImpl; +import android.app.trust.TrustManager; +import android.app.usage.IStorageStatsManager; +import android.app.usage.IUsageStatsManager; +import android.app.usage.NetworkStatsManager; +import android.app.usage.StorageStatsManager; +import android.app.usage.UsageStatsManager; +import android.apphibernation.AppHibernationManager; +import android.appwidget.AppWidgetManager; +import android.bluetooth.BluetoothManager; +import android.companion.CompanionDeviceManager; +import android.companion.ICompanionDeviceManager; +import android.content.ClipboardManager; +import android.content.ContentCaptureOptions; +import android.content.Context; +import android.content.IRestrictionsManager; +import android.content.RestrictionsManager; +import android.content.integrity.AppIntegrityManager; +import android.content.integrity.IAppIntegrityManager; +import android.content.om.IOverlayManager; +import android.content.om.OverlayManager; +import android.content.pm.ApplicationInfo; +import android.content.pm.CrossProfileApps; +import android.content.pm.DataLoaderManager; +import android.content.pm.ICrossProfileApps; +import android.content.pm.IDataLoaderManager; +import android.content.pm.IShortcutService; +import android.content.pm.LauncherApps; +import android.content.pm.PackageManager; +import android.content.pm.ShortcutManager; +import android.content.pm.verify.domain.DomainVerificationManager; +import android.content.pm.verify.domain.IDomainVerificationManager; +import android.content.res.Resources; +import android.content.rollback.RollbackManagerFrameworkInitializer; +import android.debug.AdbManager; +import android.debug.IAdbManager; +import android.graphics.fonts.FontManager; +import android.hardware.ConsumerIrManager; +import android.hardware.ISerialManager; +import android.hardware.SensorManager; +import android.hardware.SensorPrivacyManager; +import android.hardware.SerialManager; +import android.hardware.SystemSensorManager; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.IAuthService; +import android.hardware.camera2.CameraManager; +import android.hardware.devicestate.DeviceStateManager; +import android.hardware.display.ColorDisplayManager; +import android.hardware.display.DisplayManager; +import android.hardware.face.FaceManager; +import android.hardware.face.IFaceService; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.IFingerprintService; +import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.IHdmiControlService; +import android.hardware.input.InputManager; +import android.hardware.iris.IIrisService; +import android.hardware.iris.IrisManager; +import android.hardware.lights.LightsManager; +import android.hardware.lights.SystemLightsManager; +import android.hardware.location.ContextHubManager; +import android.hardware.radio.RadioManager; +import android.hardware.usb.IUsbManager; +import android.hardware.usb.UsbManager; +import android.location.CountryDetector; +import android.location.ICountryDetector; +import android.location.ILocationManager; +import android.location.LocationManager; +import android.media.AudioManager; +import android.media.MediaFrameworkInitializer; +import android.media.MediaFrameworkPlatformInitializer; +import android.media.MediaRouter; +import android.media.metrics.IMediaMetricsManager; +import android.media.metrics.MediaMetricsManager; +import android.media.midi.IMidiManager; +import android.media.midi.MidiManager; +import android.media.musicrecognition.IMusicRecognitionManager; +import android.media.musicrecognition.MusicRecognitionManager; +import android.media.projection.MediaProjectionManager; +import android.media.soundtrigger.SoundTriggerManager; +import android.media.tv.ITvInputManager; +import android.media.tv.TvInputManager; +import android.media.tv.tunerresourcemanager.ITunerResourceManager; +import android.media.tv.tunerresourcemanager.TunerResourceManager; +import android.net.ConnectivityFrameworkInitializer; +import android.net.EthernetManager; +import android.net.IEthernetManager; +import android.net.IIpSecService; +import android.net.INetworkPolicyManager; +import android.net.IPacProxyManager; +import android.net.IVpnManager; +import android.net.IpSecManager; +import android.net.NetworkPolicyManager; +import android.net.NetworkScoreManager; +import android.net.NetworkWatchlistManager; +import android.net.PacProxyManager; +import android.net.TetheringManager; +import android.net.VpnManager; +import android.net.lowpan.ILowpanManager; +import android.net.lowpan.LowpanManager; +import android.net.nsd.INsdManager; +import android.net.nsd.NsdManager; +import android.net.vcn.IVcnManagementService; +import android.net.vcn.VcnManager; +import android.net.wifi.WifiFrameworkInitializer; +import android.net.wifi.nl80211.WifiNl80211Manager; +import android.nfc.NfcManager; +import android.os.BatteryManager; +import android.os.BatteryStats; +import android.os.BatteryStatsManager; +import android.os.BugreportManager; +import android.os.Build; +import android.os.DropBoxManager; +import android.os.HardwarePropertiesManager; +import android.os.IBatteryPropertiesRegistrar; +import android.os.IBinder; +import android.os.IDumpstate; +import android.os.IHardwarePropertiesManager; +import android.os.IPowerManager; +import android.os.IRecoverySystem; +import android.os.ISystemUpdateManager; +import android.os.IThermalService; +import android.os.IUserManager; +import android.os.IncidentManager; +import android.os.MikRomManager; +import android.os.PerformanceHintManager; +import android.os.PowerManager; +import android.os.RecoverySystem; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; +import android.os.StatsFrameworkInitializer; +import android.os.SystemConfigManager; +import android.os.SystemUpdateManager; +import android.os.SystemVibrator; +import android.os.SystemVibratorManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.Vibrator; +import android.os.VibratorManager; +import android.os.health.SystemHealthManager; +import android.os.image.DynamicSystemManager; +import android.os.image.IDynamicSystemService; +import android.os.incremental.IIncrementalService; +import android.os.incremental.IncrementalManager; +import android.os.storage.StorageManager; +import android.permission.LegacyPermissionManager; +import android.permission.PermissionCheckerManager; +import android.permission.PermissionControllerManager; +import android.permission.PermissionManager; +import android.print.IPrintManager; +import android.print.PrintManager; +import android.scheduling.SchedulingFrameworkInitializer; +import android.security.FileIntegrityManager; +import android.security.IFileIntegrityService; +import android.service.oemlock.IOemLockService; +import android.service.oemlock.OemLockManager; +import android.service.persistentdata.IPersistentDataBlockService; +import android.service.persistentdata.PersistentDataBlockManager; +import android.service.vr.IVrManager; +import android.telecom.TelecomManager; +import android.telephony.MmsManager; +import android.telephony.TelephonyFrameworkInitializer; +import android.telephony.TelephonyRegistryManager; +import android.util.ArrayMap; +import android.util.Log; +import android.util.Slog; +import android.uwb.UwbManager; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.WindowManager; +import android.view.WindowManagerImpl; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.CaptioningManager; +import android.view.autofill.AutofillManager; +import android.view.autofill.IAutoFillManager; +import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.IContentCaptureManager; +import android.view.displayhash.DisplayHashManager; +import android.view.inputmethod.InputMethodManager; +import android.view.textclassifier.TextClassificationManager; +import android.view.textservice.TextServicesManager; +import android.view.translation.ITranslationManager; +import android.view.translation.TranslationManager; +import android.view.translation.UiTranslationManager; + +import com.android.internal.app.IAppOpsService; +import com.android.internal.app.IBatteryStats; +import com.android.internal.app.ISoundTriggerService; +import com.android.internal.appwidget.IAppWidgetService; +import com.android.internal.graphics.fonts.IFontManager; +import com.android.internal.net.INetworkWatchlistManager; +import com.android.internal.os.IDropBoxManagerService; +import com.android.internal.policy.PhoneLayoutInflater; +import com.android.internal.util.Preconditions; + +import java.util.Map; +import java.util.Objects; + +/** + * Manages all of the system services that can be returned by {@link Context#getSystemService}. + * Used by {@link ContextImpl}. + * + * @hide + */ +@SystemApi +public final class SystemServiceRegistry { + private static final String TAG = "SystemServiceRegistry"; + + /** @hide */ + public static boolean sEnableServiceNotFoundWtf = false; + + // Service registry information. + // This information is never changed once static initialization has completed. + private static final Map, String> SYSTEM_SERVICE_NAMES = + new ArrayMap, String>(); + private static final Map> SYSTEM_SERVICE_FETCHERS = + new ArrayMap>(); + private static final Map SYSTEM_SERVICE_CLASS_NAMES = new ArrayMap<>(); + + private static int sServiceCacheSize; + + private static volatile boolean sInitializing; + + // Not instantiable. + private SystemServiceRegistry() { } + + static { + //CHECKSTYLE:OFF IndentationCheck + registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class, + new CachedServiceFetcher() { + @Override + public AccessibilityManager createService(ContextImpl ctx) { + return AccessibilityManager.getInstance(ctx); + }}); + + registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class, + new CachedServiceFetcher() { + @Override + public CaptioningManager createService(ContextImpl ctx) { + return new CaptioningManager(ctx); + }}); + + registerService(Context.ACCOUNT_SERVICE, AccountManager.class, + new CachedServiceFetcher() { + @Override + public AccountManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.ACCOUNT_SERVICE); + IAccountManager service = IAccountManager.Stub.asInterface(b); + return new AccountManager(ctx, service); + }}); + + registerService(Context.ACTIVITY_SERVICE, ActivityManager.class, + new CachedServiceFetcher() { + @Override + public ActivityManager createService(ContextImpl ctx) { + return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler()); + }}); + + registerService(Context.ACTIVITY_TASK_SERVICE, ActivityTaskManager.class, + new CachedServiceFetcher() { + @Override + public ActivityTaskManager createService(ContextImpl ctx) { + return ActivityTaskManager.getInstance(); + }}); + + registerService(Context.URI_GRANTS_SERVICE, UriGrantsManager.class, + new CachedServiceFetcher() { + @Override + public UriGrantsManager createService(ContextImpl ctx) { + return new UriGrantsManager( + ctx.getOuterContext(), ctx.mMainThread.getHandler()); + }}); + + registerService(Context.ALARM_SERVICE, AlarmManager.class, + new CachedServiceFetcher() { + @Override + public AlarmManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.ALARM_SERVICE); + IAlarmManager service = IAlarmManager.Stub.asInterface(b); + return new AlarmManager(service, ctx); + }}); + + registerService(Context.AUDIO_SERVICE, AudioManager.class, + new CachedServiceFetcher() { + @Override + public AudioManager createService(ContextImpl ctx) { + return new AudioManager(ctx); + }}); + + registerService(Context.MEDIA_ROUTER_SERVICE, MediaRouter.class, + new CachedServiceFetcher() { + @Override + public MediaRouter createService(ContextImpl ctx) { + return new MediaRouter(ctx); + }}); + + registerService(Context.BLUETOOTH_SERVICE, BluetoothManager.class, + new CachedServiceFetcher() { + @Override + public BluetoothManager createService(ContextImpl ctx) { + return new BluetoothManager(ctx); + }}); + + registerService(Context.HDMI_CONTROL_SERVICE, HdmiControlManager.class, + new StaticServiceFetcher() { + @Override + public HdmiControlManager createService() throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.HDMI_CONTROL_SERVICE); + return new HdmiControlManager(IHdmiControlService.Stub.asInterface(b)); + }}); + + registerService(Context.TEXT_CLASSIFICATION_SERVICE, TextClassificationManager.class, + new CachedServiceFetcher() { + @Override + public TextClassificationManager createService(ContextImpl ctx) { + return new TextClassificationManager(ctx); + }}); + + registerService(Context.FONT_SERVICE, FontManager.class, + new CachedServiceFetcher() { + @Override + public FontManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.FONT_SERVICE); + return FontManager.create(IFontManager.Stub.asInterface(b)); + }}); + + registerService(Context.CLIPBOARD_SERVICE, ClipboardManager.class, + new CachedServiceFetcher() { + @Override + public ClipboardManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new ClipboardManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler()); + }}); + + // The clipboard service moved to a new package. If someone asks for the old + // interface by class then we want to redirect over to the new interface instead + // (which extends it). + SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE); + + registerService(Context.PAC_PROXY_SERVICE, PacProxyManager.class, + new CachedServiceFetcher() { + @Override + public PacProxyManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.PAC_PROXY_SERVICE); + IPacProxyManager service = IPacProxyManager.Stub.asInterface(b); + return new PacProxyManager(ctx.getOuterContext(), service); + }}); + + registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher() { + @Override + public IBinder createService() throws ServiceNotFoundException { + return ServiceManager.getServiceOrThrow(Context.NETD_SERVICE); + } + }); + + registerService(Context.TETHERING_SERVICE, TetheringManager.class, + new CachedServiceFetcher() { + @Override + public TetheringManager createService(ContextImpl ctx) { + return new TetheringManager( + ctx, () -> ServiceManager.getService(Context.TETHERING_SERVICE)); + }}); + + registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class, + new CachedServiceFetcher() { + @Override + public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.VPN_MANAGEMENT_SERVICE); + IVpnManager service = IVpnManager.Stub.asInterface(b); + return new VpnManager(ctx, service); + }}); + + registerService(Context.VCN_MANAGEMENT_SERVICE, VcnManager.class, + new CachedServiceFetcher() { + @Override + public VcnManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.VCN_MANAGEMENT_SERVICE); + IVcnManagementService service = IVcnManagementService.Stub.asInterface(b); + return new VcnManager(ctx, service); + }}); + + registerService(Context.IPSEC_SERVICE, IpSecManager.class, + new CachedServiceFetcher() { + @Override + public IpSecManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE); + IIpSecService service = IIpSecService.Stub.asInterface(b); + return new IpSecManager(ctx, service); + }}); + + registerService(Context.COUNTRY_DETECTOR, CountryDetector.class, + new StaticServiceFetcher() { + @Override + public CountryDetector createService() throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.COUNTRY_DETECTOR); + return new CountryDetector(ICountryDetector.Stub.asInterface(b)); + }}); + + registerService(Context.DEVICE_POLICY_SERVICE, DevicePolicyManager.class, + new CachedServiceFetcher() { + @Override + public DevicePolicyManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.DEVICE_POLICY_SERVICE); + return new DevicePolicyManager(ctx, IDevicePolicyManager.Stub.asInterface(b)); + }}); + + registerService(Context.DOWNLOAD_SERVICE, DownloadManager.class, + new CachedServiceFetcher() { + @Override + public DownloadManager createService(ContextImpl ctx) { + return new DownloadManager(ctx); + }}); + + registerService(Context.BATTERY_SERVICE, BatteryManager.class, + new CachedServiceFetcher() { + @Override + public BatteryManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBatteryStats stats = IBatteryStats.Stub.asInterface( + ServiceManager.getServiceOrThrow(BatteryStats.SERVICE_NAME)); + IBatteryPropertiesRegistrar registrar = IBatteryPropertiesRegistrar.Stub + .asInterface(ServiceManager.getServiceOrThrow("batteryproperties")); + return new BatteryManager(ctx, stats, registrar); + }}); + + registerService(Context.NFC_SERVICE, NfcManager.class, + new CachedServiceFetcher() { + @Override + public NfcManager createService(ContextImpl ctx) { + return new NfcManager(ctx); + }}); + + registerService(Context.DROPBOX_SERVICE, DropBoxManager.class, + new CachedServiceFetcher() { + @Override + public DropBoxManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.DROPBOX_SERVICE); + IDropBoxManagerService service = IDropBoxManagerService.Stub.asInterface(b); + return new DropBoxManager(ctx, service); + }}); + + registerService(Context.INPUT_SERVICE, InputManager.class, + new StaticServiceFetcher() { + @Override + public InputManager createService() { + return InputManager.getInstance(); + }}); + + registerService(Context.DISPLAY_SERVICE, DisplayManager.class, + new CachedServiceFetcher() { + @Override + public DisplayManager createService(ContextImpl ctx) { + return new DisplayManager(ctx.getOuterContext()); + }}); + + registerService(Context.COLOR_DISPLAY_SERVICE, ColorDisplayManager.class, + new CachedServiceFetcher() { + @Override + public ColorDisplayManager createService(ContextImpl ctx) { + return new ColorDisplayManager(); + } + }); + + // InputMethodManager has its own cache strategy based on display id to support apps that + // still assume InputMethodManager is a per-process singleton and it's safe to directly + // access internal fields via reflection. Hence directly use ServiceFetcher instead of + // StaticServiceFetcher/CachedServiceFetcher. + registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class, + new ServiceFetcher() { + @Override + public InputMethodManager getService(ContextImpl ctx) { + return InputMethodManager.forContext(ctx.getOuterContext()); + }}); + + registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class, + new CachedServiceFetcher() { + @Override + public TextServicesManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return TextServicesManager.createInstance(ctx); + }}); + + registerService(Context.KEYGUARD_SERVICE, KeyguardManager.class, + new CachedServiceFetcher() { + @Override + public KeyguardManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new KeyguardManager(ctx); + }}); + + registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, + new CachedServiceFetcher() { + @Override + public LayoutInflater createService(ContextImpl ctx) { + return new PhoneLayoutInflater(ctx.getOuterContext()); + }}); + + registerService(Context.LOCATION_SERVICE, LocationManager.class, + new CachedServiceFetcher() { + @Override + public LocationManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE); + return new LocationManager(ctx, ILocationManager.Stub.asInterface(b)); + }}); + + registerService(Context.NETWORK_POLICY_SERVICE, NetworkPolicyManager.class, + new CachedServiceFetcher() { + @Override + public NetworkPolicyManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new NetworkPolicyManager(ctx, INetworkPolicyManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.NETWORK_POLICY_SERVICE))); + }}); + + registerService(Context.NOTIFICATION_SERVICE, NotificationManager.class, + new CachedServiceFetcher() { + @Override + public NotificationManager createService(ContextImpl ctx) { + final Context outerContext = ctx.getOuterContext(); + return new NotificationManager( + new ContextThemeWrapper(outerContext, + Resources.selectSystemTheme(0, + outerContext.getApplicationInfo().targetSdkVersion, + com.android.internal.R.style.Theme_Dialog, + com.android.internal.R.style.Theme_Holo_Dialog, + com.android.internal.R.style.Theme_DeviceDefault_Dialog, + com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog)), + ctx.mMainThread.getHandler()); + }}); + + registerService(Context.NSD_SERVICE, NsdManager.class, + new CachedServiceFetcher() { + @Override + public NsdManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.NSD_SERVICE); + INsdManager service = INsdManager.Stub.asInterface(b); + return new NsdManager(ctx.getOuterContext(), service); + }}); + + registerService(Context.PEOPLE_SERVICE, PeopleManager.class, + new CachedServiceFetcher() { + @Override + public PeopleManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new PeopleManager(ctx); + }}); + + registerService(Context.POWER_SERVICE, PowerManager.class, + new CachedServiceFetcher() { + @Override + public PowerManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder powerBinder = ServiceManager.getServiceOrThrow(Context.POWER_SERVICE); + IPowerManager powerService = IPowerManager.Stub.asInterface(powerBinder); + IBinder thermalBinder = ServiceManager.getServiceOrThrow(Context.THERMAL_SERVICE); + IThermalService thermalService = IThermalService.Stub.asInterface(thermalBinder); + return new PowerManager(ctx.getOuterContext(), powerService, thermalService, + ctx.mMainThread.getHandler()); + }}); + + registerService(Context.MIKROM_SERVICE, MikRomManager.class, + new CachedServiceFetcher() { + @Override + public MikRomManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return MikRomManager.getInstance(); + }}); + + registerService(Context.PERFORMANCE_HINT_SERVICE, PerformanceHintManager.class, + new CachedServiceFetcher() { + @Override + public PerformanceHintManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return PerformanceHintManager.create(); + }}); + + registerService(Context.RECOVERY_SERVICE, RecoverySystem.class, + new CachedServiceFetcher() { + @Override + public RecoverySystem createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.RECOVERY_SERVICE); + IRecoverySystem service = IRecoverySystem.Stub.asInterface(b); + return new RecoverySystem(service); + }}); + + registerService(Context.SEARCH_SERVICE, SearchManager.class, + new CachedServiceFetcher() { + @Override + public SearchManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new SearchManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler()); + }}); + + registerService(Context.SENSOR_SERVICE, SensorManager.class, + new CachedServiceFetcher() { + @Override + public SensorManager createService(ContextImpl ctx) { + return new SystemSensorManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler().getLooper()); + }}); + + registerService(Context.SENSOR_PRIVACY_SERVICE, SensorPrivacyManager.class, + new CachedServiceFetcher() { + @Override + public SensorPrivacyManager createService(ContextImpl ctx) { + return SensorPrivacyManager.getInstance(ctx); + }}); + + registerService(Context.STATUS_BAR_SERVICE, StatusBarManager.class, + new CachedServiceFetcher() { + @Override + public StatusBarManager createService(ContextImpl ctx) { + return new StatusBarManager(ctx.getOuterContext()); + }}); + + registerService(Context.STORAGE_SERVICE, StorageManager.class, + new CachedServiceFetcher() { + @Override + public StorageManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new StorageManager(ctx, ctx.mMainThread.getHandler().getLooper()); + }}); + + registerService(Context.STORAGE_STATS_SERVICE, StorageStatsManager.class, + new CachedServiceFetcher() { + @Override + public StorageStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IStorageStatsManager service = IStorageStatsManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.STORAGE_STATS_SERVICE)); + return new StorageStatsManager(ctx, service); + }}); + + registerService(Context.SYSTEM_UPDATE_SERVICE, SystemUpdateManager.class, + new CachedServiceFetcher() { + @Override + public SystemUpdateManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow( + Context.SYSTEM_UPDATE_SERVICE); + ISystemUpdateManager service = ISystemUpdateManager.Stub.asInterface(b); + return new SystemUpdateManager(service); + }}); + + registerService(Context.SYSTEM_CONFIG_SERVICE, SystemConfigManager.class, + new CachedServiceFetcher() { + @Override + public SystemConfigManager createService(ContextImpl ctx) { + return new SystemConfigManager(); + }}); + + registerService(Context.TELEPHONY_REGISTRY_SERVICE, TelephonyRegistryManager.class, + new CachedServiceFetcher() { + @Override + public TelephonyRegistryManager createService(ContextImpl ctx) { + return new TelephonyRegistryManager(ctx); + }}); + + registerService(Context.TELECOM_SERVICE, TelecomManager.class, + new CachedServiceFetcher() { + @Override + public TelecomManager createService(ContextImpl ctx) { + return new TelecomManager(ctx.getOuterContext()); + }}); + + registerService(Context.MMS_SERVICE, MmsManager.class, + new CachedServiceFetcher() { + @Override + public MmsManager createService(ContextImpl ctx) { + return new MmsManager(ctx.getOuterContext()); + }}); + + registerService(Context.UI_MODE_SERVICE, UiModeManager.class, + new CachedServiceFetcher() { + @Override + public UiModeManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new UiModeManager(ctx.getOuterContext()); + }}); + + registerService(Context.USB_SERVICE, UsbManager.class, + new CachedServiceFetcher() { + @Override + public UsbManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.USB_SERVICE); + return new UsbManager(ctx, IUsbManager.Stub.asInterface(b)); + }}); + + registerService(Context.ADB_SERVICE, AdbManager.class, + new CachedServiceFetcher() { + @Override + public AdbManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.ADB_SERVICE); + return new AdbManager(ctx, IAdbManager.Stub.asInterface(b)); + }}); + + registerService(Context.SERIAL_SERVICE, SerialManager.class, + new CachedServiceFetcher() { + @Override + public SerialManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.SERIAL_SERVICE); + return new SerialManager(ctx, ISerialManager.Stub.asInterface(b)); + }}); + + registerService(Context.UWB_SERVICE, UwbManager.class, + new CachedServiceFetcher() { + @Override + public UwbManager createService(ContextImpl ctx) { + return UwbManager.getInstance(ctx); + } + }); + + registerService(Context.VIBRATOR_MANAGER_SERVICE, VibratorManager.class, + new CachedServiceFetcher() { + @Override + public VibratorManager createService(ContextImpl ctx) { + return new SystemVibratorManager(ctx); + }}); + + registerService(Context.VIBRATOR_SERVICE, Vibrator.class, + new CachedServiceFetcher() { + @Override + public Vibrator createService(ContextImpl ctx) { + return new SystemVibrator(ctx); + }}); + + registerService(Context.WALLPAPER_SERVICE, WallpaperManager.class, + new CachedServiceFetcher() { + @Override + public WallpaperManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + final IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE); + if (b == null) { + ApplicationInfo appInfo = ctx.getApplicationInfo(); + if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P + && appInfo.isInstantApp()) { + // Instant app + throw new ServiceNotFoundException(Context.WALLPAPER_SERVICE); + } + final boolean enabled = Resources.getSystem() + .getBoolean(com.android.internal.R.bool.config_enableWallpaperService); + if (!enabled) { + // Device doesn't support wallpaper, return a limited manager + return DisabledWallpaperManager.getInstance(); + } + // Bad state - WallpaperManager methods will throw exception + Log.e(TAG, "No wallpaper service"); + } + IWallpaperManager service = IWallpaperManager.Stub.asInterface(b); + return new WallpaperManager(service, ctx.getOuterContext(), + ctx.mMainThread.getHandler()); + }}); + + registerService(Context.LOWPAN_SERVICE, LowpanManager.class, + new CachedServiceFetcher() { + @Override + public LowpanManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.LOWPAN_SERVICE); + ILowpanManager service = ILowpanManager.Stub.asInterface(b); + return new LowpanManager(ctx.getOuterContext(), service); + }}); + + registerService(Context.ETHERNET_SERVICE, EthernetManager.class, + new CachedServiceFetcher() { + @Override + public EthernetManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.ETHERNET_SERVICE); + IEthernetManager service = IEthernetManager.Stub.asInterface(b); + return new EthernetManager(ctx.getOuterContext(), service); + }}); + + registerService(Context.WIFI_NL80211_SERVICE, WifiNl80211Manager.class, + new CachedServiceFetcher() { + @Override + public WifiNl80211Manager createService(ContextImpl ctx) { + return new WifiNl80211Manager(ctx.getOuterContext()); + } + }); + + registerService(Context.WINDOW_SERVICE, WindowManager.class, + new CachedServiceFetcher() { + @Override + public WindowManager createService(ContextImpl ctx) { + return new WindowManagerImpl(ctx); + }}); + + registerService(Context.USER_SERVICE, UserManager.class, + new CachedServiceFetcher() { + @Override + public UserManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.USER_SERVICE); + IUserManager service = IUserManager.Stub.asInterface(b); + return new UserManager(ctx, service); + }}); + + registerService(Context.APP_OPS_SERVICE, AppOpsManager.class, + new CachedServiceFetcher() { + @Override + public AppOpsManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.APP_OPS_SERVICE); + IAppOpsService service = IAppOpsService.Stub.asInterface(b); + return new AppOpsManager(ctx, service); + }}); + + registerService(Context.CAMERA_SERVICE, CameraManager.class, + new CachedServiceFetcher() { + @Override + public CameraManager createService(ContextImpl ctx) { + return new CameraManager(ctx); + }}); + + registerService(Context.LAUNCHER_APPS_SERVICE, LauncherApps.class, + new CachedServiceFetcher() { + @Override + public LauncherApps createService(ContextImpl ctx) { + return new LauncherApps(ctx); + }}); + + registerService(Context.RESTRICTIONS_SERVICE, RestrictionsManager.class, + new CachedServiceFetcher() { + @Override + public RestrictionsManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.RESTRICTIONS_SERVICE); + IRestrictionsManager service = IRestrictionsManager.Stub.asInterface(b); + return new RestrictionsManager(ctx, service); + }}); + + registerService(Context.PRINT_SERVICE, PrintManager.class, + new CachedServiceFetcher() { + @Override + public PrintManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IPrintManager service = null; + // If the feature not present, don't try to look up every time + if (ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING)) { + service = IPrintManager.Stub.asInterface(ServiceManager + .getServiceOrThrow(Context.PRINT_SERVICE)); + } + final int userId = ctx.getUserId(); + final int appId = UserHandle.getAppId(ctx.getApplicationInfo().uid); + return new PrintManager(ctx.getOuterContext(), service, userId, appId); + }}); + + registerService(Context.COMPANION_DEVICE_SERVICE, CompanionDeviceManager.class, + new CachedServiceFetcher() { + @Override + public CompanionDeviceManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + ICompanionDeviceManager service = null; + // If the feature not present, don't try to look up every time + if (ctx.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) { + service = ICompanionDeviceManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.COMPANION_DEVICE_SERVICE)); + } + return new CompanionDeviceManager(service, ctx.getOuterContext()); + }}); + + registerService(Context.CONSUMER_IR_SERVICE, ConsumerIrManager.class, + new CachedServiceFetcher() { + @Override + public ConsumerIrManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new ConsumerIrManager(ctx); + }}); + + registerService(Context.TRUST_SERVICE, TrustManager.class, + new StaticServiceFetcher() { + @Override + public TrustManager createService() throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.TRUST_SERVICE); + return new TrustManager(b); + }}); + + registerService(Context.FINGERPRINT_SERVICE, FingerprintManager.class, + new CachedServiceFetcher() { + @Override + public FingerprintManager createService(ContextImpl ctx) throws ServiceNotFoundException { + final IBinder binder; + if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) { + binder = ServiceManager.getServiceOrThrow(Context.FINGERPRINT_SERVICE); + } else { + binder = ServiceManager.getService(Context.FINGERPRINT_SERVICE); + } + IFingerprintService service = IFingerprintService.Stub.asInterface(binder); + return new FingerprintManager(ctx.getOuterContext(), service); + }}); + + registerService(Context.FACE_SERVICE, FaceManager.class, + new CachedServiceFetcher() { + @Override + public FaceManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + final IBinder binder; + if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) { + binder = ServiceManager.getServiceOrThrow(Context.FACE_SERVICE); + } else { + binder = ServiceManager.getService(Context.FACE_SERVICE); + } + IFaceService service = IFaceService.Stub.asInterface(binder); + return new FaceManager(ctx.getOuterContext(), service); + } + }); + + registerService(Context.IRIS_SERVICE, IrisManager.class, + new CachedServiceFetcher() { + @Override + public IrisManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + final IBinder binder = + ServiceManager.getServiceOrThrow(Context.IRIS_SERVICE); + IIrisService service = IIrisService.Stub.asInterface(binder); + return new IrisManager(ctx.getOuterContext(), service); + } + }); + + registerService(Context.BIOMETRIC_SERVICE, BiometricManager.class, + new CachedServiceFetcher() { + @Override + public BiometricManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + final IBinder binder = + ServiceManager.getServiceOrThrow(Context.AUTH_SERVICE); + final IAuthService service = + IAuthService.Stub.asInterface(binder); + return new BiometricManager(ctx.getOuterContext(), service); + } + }); + + registerService(Context.TV_INPUT_SERVICE, TvInputManager.class, + new CachedServiceFetcher() { + @Override + public TvInputManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder iBinder = ServiceManager.getServiceOrThrow(Context.TV_INPUT_SERVICE); + ITvInputManager service = ITvInputManager.Stub.asInterface(iBinder); + return new TvInputManager(service, ctx.getUserId()); + }}); + + registerService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, TunerResourceManager.class, + new CachedServiceFetcher() { + @Override + public TunerResourceManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder iBinder = + ServiceManager.getServiceOrThrow(Context.TV_TUNER_RESOURCE_MGR_SERVICE); + ITunerResourceManager service = ITunerResourceManager.Stub.asInterface(iBinder); + return new TunerResourceManager(service, ctx.getUserId()); + }}); + + registerService(Context.NETWORK_SCORE_SERVICE, NetworkScoreManager.class, + new CachedServiceFetcher() { + @Override + public NetworkScoreManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new NetworkScoreManager(ctx); + }}); + + registerService(Context.USAGE_STATS_SERVICE, UsageStatsManager.class, + new CachedServiceFetcher() { + @Override + public UsageStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder iBinder = ServiceManager.getServiceOrThrow(Context.USAGE_STATS_SERVICE); + IUsageStatsManager service = IUsageStatsManager.Stub.asInterface(iBinder); + return new UsageStatsManager(ctx.getOuterContext(), service); + }}); + + registerService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class, + new CachedServiceFetcher() { + @Override + public NetworkStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new NetworkStatsManager(ctx.getOuterContext()); + }}); + + registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class, + new StaticServiceFetcher() { + @Override + public PersistentDataBlockManager createService() throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.PERSISTENT_DATA_BLOCK_SERVICE); + IPersistentDataBlockService persistentDataBlockService = + IPersistentDataBlockService.Stub.asInterface(b); + if (persistentDataBlockService != null) { + return new PersistentDataBlockManager(persistentDataBlockService); + } else { + // not supported + return null; + } + }}); + + registerService(Context.OEM_LOCK_SERVICE, OemLockManager.class, + new StaticServiceFetcher() { + @Override + public OemLockManager createService() throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.OEM_LOCK_SERVICE); + IOemLockService oemLockService = IOemLockService.Stub.asInterface(b); + if (oemLockService != null) { + return new OemLockManager(oemLockService); + } else { + // not supported + return null; + } + }}); + + registerService(Context.MEDIA_PROJECTION_SERVICE, MediaProjectionManager.class, + new CachedServiceFetcher() { + @Override + public MediaProjectionManager createService(ContextImpl ctx) { + return new MediaProjectionManager(ctx); + }}); + + registerService(Context.APPWIDGET_SERVICE, AppWidgetManager.class, + new CachedServiceFetcher() { + @Override + public AppWidgetManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.APPWIDGET_SERVICE); + return new AppWidgetManager(ctx, IAppWidgetService.Stub.asInterface(b)); + }}); + + registerService(Context.MIDI_SERVICE, MidiManager.class, + new CachedServiceFetcher() { + @Override + public MidiManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.MIDI_SERVICE); + return new MidiManager(IMidiManager.Stub.asInterface(b)); + }}); + + registerService(Context.RADIO_SERVICE, RadioManager.class, + new CachedServiceFetcher() { + @Override + public RadioManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new RadioManager(ctx); + }}); + + registerService(Context.HARDWARE_PROPERTIES_SERVICE, HardwarePropertiesManager.class, + new CachedServiceFetcher() { + @Override + public HardwarePropertiesManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.HARDWARE_PROPERTIES_SERVICE); + IHardwarePropertiesManager service = + IHardwarePropertiesManager.Stub.asInterface(b); + return new HardwarePropertiesManager(ctx, service); + }}); + + registerService(Context.SOUND_TRIGGER_SERVICE, SoundTriggerManager.class, + new CachedServiceFetcher() { + @Override + public SoundTriggerManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.SOUND_TRIGGER_SERVICE); + return new SoundTriggerManager(ctx, ISoundTriggerService.Stub.asInterface(b)); + }}); + + registerService(Context.SHORTCUT_SERVICE, ShortcutManager.class, + new CachedServiceFetcher() { + @Override + public ShortcutManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.SHORTCUT_SERVICE); + return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b)); + }}); + + registerService(Context.OVERLAY_SERVICE, OverlayManager.class, + new CachedServiceFetcher() { + @Override + public OverlayManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.OVERLAY_SERVICE); + return new OverlayManager(ctx, IOverlayManager.Stub.asInterface(b)); + }}); + + registerService(Context.NETWORK_WATCHLIST_SERVICE, NetworkWatchlistManager.class, + new CachedServiceFetcher() { + @Override + public NetworkWatchlistManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = + ServiceManager.getServiceOrThrow(Context.NETWORK_WATCHLIST_SERVICE); + return new NetworkWatchlistManager(ctx, + INetworkWatchlistManager.Stub.asInterface(b)); + }}); + + registerService(Context.SYSTEM_HEALTH_SERVICE, SystemHealthManager.class, + new CachedServiceFetcher() { + @Override + public SystemHealthManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(BatteryStats.SERVICE_NAME); + return new SystemHealthManager(IBatteryStats.Stub.asInterface(b)); + }}); + + registerService(Context.CONTEXTHUB_SERVICE, ContextHubManager.class, + new CachedServiceFetcher() { + @Override + public ContextHubManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new ContextHubManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler().getLooper()); + }}); + + registerService(Context.INCIDENT_SERVICE, IncidentManager.class, + new CachedServiceFetcher() { + @Override + public IncidentManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new IncidentManager(ctx); + }}); + + registerService(Context.BUGREPORT_SERVICE, BugreportManager.class, + new CachedServiceFetcher() { + @Override + public BugreportManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.BUGREPORT_SERVICE); + return new BugreportManager(ctx.getOuterContext(), + IDumpstate.Stub.asInterface(b)); + }}); + + registerService(Context.AUTOFILL_MANAGER_SERVICE, AutofillManager.class, + new CachedServiceFetcher() { + @Override + public AutofillManager createService(ContextImpl ctx) throws ServiceNotFoundException { + // Get the services without throwing as this is an optional feature + IBinder b = ServiceManager.getService(Context.AUTOFILL_MANAGER_SERVICE); + IAutoFillManager service = IAutoFillManager.Stub.asInterface(b); + return new AutofillManager(ctx.getOuterContext(), service); + }}); + + registerService(Context.MUSIC_RECOGNITION_SERVICE, MusicRecognitionManager.class, + new CachedServiceFetcher() { + @Override + public MusicRecognitionManager createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService( + Context.MUSIC_RECOGNITION_SERVICE); + return new MusicRecognitionManager( + IMusicRecognitionManager.Stub.asInterface(b)); + } + }); + + registerService(Context.CONTENT_CAPTURE_MANAGER_SERVICE, ContentCaptureManager.class, + new CachedServiceFetcher() { + @Override + public ContentCaptureManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + // Get the services without throwing as this is an optional feature + Context outerContext = ctx.getOuterContext(); + ContentCaptureOptions options = outerContext.getContentCaptureOptions(); + // Options is null when the service didn't allowlist the activity or package + if (options != null && (options.lite || options.isWhitelisted(outerContext))) { + IBinder b = ServiceManager + .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); + IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b); + // Service is null when not provided by OEM or disabled by kill-switch. + if (service != null) { + return new ContentCaptureManager(outerContext, service, options); + } + } + // When feature is disabled or app / package not allowlisted, we return a null + // manager to apps so the performance impact is practically zero + return null; + }}); + + registerService(Context.TRANSLATION_MANAGER_SERVICE, TranslationManager.class, + new CachedServiceFetcher() { + @Override + public TranslationManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.TRANSLATION_MANAGER_SERVICE); + ITranslationManager service = ITranslationManager.Stub.asInterface(b); + // Service is null when not provided by OEM. + if (service != null) { + return new TranslationManager(ctx.getOuterContext(), service); + } + return null; + }}); + + registerService(Context.UI_TRANSLATION_SERVICE, UiTranslationManager.class, + new CachedServiceFetcher() { + @Override + public UiTranslationManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.TRANSLATION_MANAGER_SERVICE); + ITranslationManager service = ITranslationManager.Stub.asInterface(b); + if (service != null) { + return new UiTranslationManager(ctx.getOuterContext(), service); + } + return null; + }}); + + registerService(Context.SEARCH_UI_SERVICE, SearchUiManager.class, + new CachedServiceFetcher() { + @Override + public SearchUiManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.SEARCH_UI_SERVICE); + return b == null ? null : new SearchUiManager(ctx); + } + }); + + registerService(Context.SMARTSPACE_SERVICE, SmartspaceManager.class, + new CachedServiceFetcher() { + @Override + public SmartspaceManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.SMARTSPACE_SERVICE); + return b == null ? null : new SmartspaceManager(ctx); + } + }); + + registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class, + new CachedServiceFetcher() { + @Override + public AppPredictionManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE); + return b == null ? null : new AppPredictionManager(ctx); + } + }); + + registerService(Context.CONTENT_SUGGESTIONS_SERVICE, + ContentSuggestionsManager.class, + new CachedServiceFetcher() { + @Override + public ContentSuggestionsManager createService(ContextImpl ctx) { + // No throw as this is an optional service + IBinder b = ServiceManager.getService( + Context.CONTENT_SUGGESTIONS_SERVICE); + IContentSuggestionsManager service = + IContentSuggestionsManager.Stub.asInterface(b); + return new ContentSuggestionsManager(ctx.getUserId(), service); + } + }); + + registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher() { + @Override + public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.VR_SERVICE); + return new VrManager(IVrManager.Stub.asInterface(b)); + } + }); + + registerService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, RulesManager.class, + new CachedServiceFetcher() { + @Override + public RulesManager createService(ContextImpl ctx) { + return new RulesManager(ctx.getOuterContext()); + }}); + + registerService(Context.CROSS_PROFILE_APPS_SERVICE, CrossProfileApps.class, + new CachedServiceFetcher() { + @Override + public CrossProfileApps createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow( + Context.CROSS_PROFILE_APPS_SERVICE); + return new CrossProfileApps(ctx.getOuterContext(), + ICrossProfileApps.Stub.asInterface(b)); + } + }); + + registerService(Context.SLICE_SERVICE, SliceManager.class, + new CachedServiceFetcher() { + @Override + public SliceManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new SliceManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler()); + } + }); + + registerService(Context.TIME_DETECTOR_SERVICE, TimeDetector.class, + new CachedServiceFetcher() { + @Override + public TimeDetector createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new TimeDetectorImpl(); + }}); + + registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class, + new CachedServiceFetcher() { + @Override + public TimeZoneDetector createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new TimeZoneDetectorImpl(); + }}); + + registerService(Context.TIME_MANAGER, TimeManager.class, + new CachedServiceFetcher() { + @Override + public TimeManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new TimeManager(); + }}); + + registerService(Context.PERMISSION_SERVICE, PermissionManager.class, + new CachedServiceFetcher() { + @Override + public PermissionManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new PermissionManager(ctx.getOuterContext()); + }}); + + registerService(Context.LEGACY_PERMISSION_SERVICE, LegacyPermissionManager.class, + new CachedServiceFetcher() { + @Override + public LegacyPermissionManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new LegacyPermissionManager(); + }}); + + registerService(Context.PERMISSION_CONTROLLER_SERVICE, PermissionControllerManager.class, + new CachedServiceFetcher() { + @Override + public PermissionControllerManager createService(ContextImpl ctx) { + return new PermissionControllerManager(ctx.getOuterContext(), + ctx.getMainThreadHandler()); + }}); + + registerService(Context.PERMISSION_CHECKER_SERVICE, PermissionCheckerManager.class, + new CachedServiceFetcher() { + @Override + public PermissionCheckerManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new PermissionCheckerManager(ctx.getOuterContext()); + }}); + + registerService(Context.DYNAMIC_SYSTEM_SERVICE, DynamicSystemManager.class, + new CachedServiceFetcher() { + @Override + public DynamicSystemManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow( + Context.DYNAMIC_SYSTEM_SERVICE); + return new DynamicSystemManager( + IDynamicSystemService.Stub.asInterface(b)); + }}); + + registerService(Context.BATTERY_STATS_SERVICE, BatteryStatsManager.class, + new CachedServiceFetcher() { + @Override + public BatteryStatsManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow( + Context.BATTERY_STATS_SERVICE); + return new BatteryStatsManager( + IBatteryStats.Stub.asInterface(b)); + }}); + registerService(Context.DATA_LOADER_MANAGER_SERVICE, DataLoaderManager.class, + new CachedServiceFetcher() { + @Override + public DataLoaderManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow( + Context.DATA_LOADER_MANAGER_SERVICE); + return new DataLoaderManager(IDataLoaderManager.Stub.asInterface(b)); + }}); + registerService(Context.LIGHTS_SERVICE, LightsManager.class, + new CachedServiceFetcher() { + @Override + public LightsManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new SystemLightsManager(ctx); + }}); + registerService(Context.INCREMENTAL_SERVICE, IncrementalManager.class, + new CachedServiceFetcher() { + @Override + public IncrementalManager createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(Context.INCREMENTAL_SERVICE); + if (b == null) { + return null; + } + return new IncrementalManager( + IIncrementalService.Stub.asInterface(b)); + }}); + + registerService(Context.FILE_INTEGRITY_SERVICE, FileIntegrityManager.class, + new CachedServiceFetcher() { + @Override + public FileIntegrityManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow( + Context.FILE_INTEGRITY_SERVICE); + return new FileIntegrityManager(ctx.getOuterContext(), + IFileIntegrityService.Stub.asInterface(b)); + }}); + //CHECKSTYLE:ON IndentationCheck + registerService(Context.APP_INTEGRITY_SERVICE, AppIntegrityManager.class, + new CachedServiceFetcher() { + @Override + public AppIntegrityManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE); + return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b)); + }}); + registerService(Context.APP_HIBERNATION_SERVICE, AppHibernationManager.class, + new CachedServiceFetcher() { + @Override + public AppHibernationManager createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(Context.APP_HIBERNATION_SERVICE); + return b == null ? null : new AppHibernationManager(ctx); + }}); + registerService(Context.DREAM_SERVICE, DreamManager.class, + new CachedServiceFetcher() { + @Override + public DreamManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new DreamManager(ctx); + }}); + registerService(Context.DEVICE_STATE_SERVICE, DeviceStateManager.class, + new CachedServiceFetcher() { + @Override + public DeviceStateManager createService(ContextImpl ctx) { + return new DeviceStateManager(); + }}); + + registerService(Context.MEDIA_METRICS_SERVICE, MediaMetricsManager.class, + new CachedServiceFetcher() { + @Override + public MediaMetricsManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder iBinder = + ServiceManager.getServiceOrThrow(Context.MEDIA_METRICS_SERVICE); + IMediaMetricsManager service = + IMediaMetricsManager.Stub.asInterface(iBinder); + return new MediaMetricsManager(service, ctx.getUserId()); + }}); + + registerService(Context.GAME_SERVICE, GameManager.class, + new CachedServiceFetcher() { + @Override + public GameManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new GameManager(ctx.getOuterContext(), + ctx.mMainThread.getHandler()); + } + }); + + registerService(Context.DOMAIN_VERIFICATION_SERVICE, DomainVerificationManager.class, + new CachedServiceFetcher() { + @Override + public DomainVerificationManager createService(ContextImpl context) + throws ServiceNotFoundException { + IBinder binder = ServiceManager.getServiceOrThrow( + Context.DOMAIN_VERIFICATION_SERVICE); + IDomainVerificationManager service = + IDomainVerificationManager.Stub.asInterface(binder); + return new DomainVerificationManager(context, service); + } + }); + + registerService(Context.DISPLAY_HASH_SERVICE, DisplayHashManager.class, + new CachedServiceFetcher() { + @Override + public DisplayHashManager createService(ContextImpl ctx) { + return new DisplayHashManager(); + }}); + + sInitializing = true; + try { + // Note: the following functions need to be @SystemApis, once they become mainline + // modules. + ConnectivityFrameworkInitializer.registerServiceWrappers(); + JobSchedulerFrameworkInitializer.registerServiceWrappers(); + BlobStoreManagerFrameworkInitializer.initialize(); + TelephonyFrameworkInitializer.registerServiceWrappers(); + AppSearchManagerFrameworkInitializer.initialize(); + WifiFrameworkInitializer.registerServiceWrappers(); + StatsFrameworkInitializer.registerServiceWrappers(); + RollbackManagerFrameworkInitializer.initialize(); + MediaFrameworkPlatformInitializer.registerServiceWrappers(); + MediaFrameworkInitializer.registerServiceWrappers(); + RoleFrameworkInitializer.registerServiceWrappers(); + SchedulingFrameworkInitializer.registerServiceWrappers(); + } finally { + // If any of the above code throws, we're in a pretty bad shape and the process + // will likely crash, but we'll reset it just in case there's an exception handler... + sInitializing = false; + } + } + + /** Throws {@link IllegalStateException} if not during a static initialization. */ + private static void ensureInitializing(String methodName) { + Preconditions.checkState(sInitializing, "Internal error: %s" + + " can only be called during class initialization.", methodName); + } + /** + * Creates an array which is used to cache per-Context service instances. + * @hide + */ + public static Object[] createServiceCache() { + return new Object[sServiceCacheSize]; + } + + /** + * Gets a system service from a given context. + * @hide + */ + public static Object getSystemService(ContextImpl ctx, String name) { + if (name == null) { + return null; + } + final ServiceFetcher fetcher = SYSTEM_SERVICE_FETCHERS.get(name); + if (fetcher == null) { + if (sEnableServiceNotFoundWtf) { + Slog.wtf(TAG, "Unknown manager requested: " + name); + } + return null; + } + + final Object ret = fetcher.getService(ctx); + if (sEnableServiceNotFoundWtf && ret == null) { + // Some services do return null in certain situations, so don't do WTF for them. + switch (name) { + case Context.CONTENT_CAPTURE_MANAGER_SERVICE: + case Context.APP_PREDICTION_SERVICE: + case Context.INCREMENTAL_SERVICE: + case Context.ETHERNET_SERVICE: + return null; + } + Slog.wtf(TAG, "Manager wrapper not available: " + name); + return null; + } + return ret; + } + + /** + * Gets the name of the system-level service that is represented by the specified class. + * @hide + */ + public static String getSystemServiceName(Class serviceClass) { + if (serviceClass == null) { + return null; + } + final String serviceName = SYSTEM_SERVICE_NAMES.get(serviceClass); + if (sEnableServiceNotFoundWtf && serviceName == null) { + // This should be a caller bug. + Slog.wtf(TAG, "Unknown manager requested: " + serviceClass.getCanonicalName()); + } + return serviceName; + } + + /** + * Statically registers a system service with the context. + * This method must be called during static initialization only. + */ + private static void registerService(@NonNull String serviceName, + @NonNull Class serviceClass, @NonNull ServiceFetcher serviceFetcher) { + SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName); + SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); + SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName()); + } + + /** + * Returns system service class name by system service name. This method is mostly an inverse of + * {@link #getSystemServiceName(Class)} + * + * @return system service class name. {@code null} if service name is invalid. + * @hide + */ + @Nullable + public static String getSystemServiceClassName(@NonNull String name) { + return SYSTEM_SERVICE_CLASS_NAMES.get(name); + } + + /** + * Callback interface used as a parameter to {@link #registerStaticService( + * String, Class, StaticServiceProducerWithoutBinder)}, which generates a service wrapper + * instance that's not tied to any context and does not take a service binder object in the + * constructor. + * + * @param type of the service wrapper class. + * + * @hide + */ + @SystemApi + public interface StaticServiceProducerWithoutBinder { + /** + * Return a new service wrapper of type {@code TServiceClass}. + */ + @NonNull + TServiceClass createService(); + } + + /** + * Callback interface used as a parameter to {@link #registerStaticService( + * String, Class, StaticServiceProducerWithBinder)}, which generates a service wrapper instance + * that's not tied to any context and takes a service binder object in the constructor. + * + * @param type of the service wrapper class. + * + * @hide + */ + @SystemApi + public interface StaticServiceProducerWithBinder { + /** + * Return a new service wrapper of type {@code TServiceClass} backed by a given + * service binder object. + */ + @NonNull + TServiceClass createService(@NonNull IBinder serviceBinder); + } + + /** + * Callback interface used as a parameter to {@link #registerContextAwareService( + * String, Class, ContextAwareServiceProducerWithoutBinder)}, + * which generates a service wrapper instance + * that's tied to a specific context and does not take a service binder object in the + * constructor. + * + * @param type of the service wrapper class. + * + * @hide + */ + @SystemApi + public interface ContextAwareServiceProducerWithoutBinder { + /** + * Return a new service wrapper of type {@code TServiceClass} tied to a given + * {@code context}. + */ + @NonNull + //TODO Do we need to pass the "base context" too? + TServiceClass createService(@NonNull Context context); + } + + /** + * Callback interface used as a parameter to {@link #registerContextAwareService( + * String, Class, ContextAwareServiceProducerWithBinder)}, + * which generates a service wrapper instance + * that's tied to a specific context and takes a service binder object in the constructor. + * + * @param type of the service wrapper class. + * + * @hide + */ + @SystemApi + public interface ContextAwareServiceProducerWithBinder { + /** + * Return a new service wrapper of type {@code TServiceClass} backed by a given + * service binder object that's tied to a given {@code context}. + */ + @NonNull + //TODO Do we need to pass the "base context" too? + TServiceClass createService(@NonNull Context context, @NonNull IBinder serviceBinder); + } + + /** + * Used by apex modules to register a "service wrapper" that is not tied to any {@link Context}. + * + *

This can only be called from the methods called by the static initializer of + * {@link SystemServiceRegistry}. (Otherwise it throws a {@link IllegalStateException}.) + * + * @param serviceName the name of the binder object, such as + * {@link Context#JOB_SCHEDULER_SERVICE}. + * @param serviceWrapperClass the wrapper class, such as the class of + * {@link android.app.job.JobScheduler}. + * @param serviceProducer Callback that takes the service binder object with the name + * {@code serviceName} and returns an actual service wrapper instance. + * + * @hide + */ + @SystemApi + public static void registerStaticService( + @NonNull String serviceName, @NonNull Class serviceWrapperClass, + @NonNull StaticServiceProducerWithBinder serviceProducer) { + ensureInitializing("registerStaticService"); + Preconditions.checkStringNotEmpty(serviceName); + Objects.requireNonNull(serviceWrapperClass); + Objects.requireNonNull(serviceProducer); + + registerService(serviceName, serviceWrapperClass, + new StaticServiceFetcher() { + @Override + public TServiceClass createService() throws ServiceNotFoundException { + return serviceProducer.createService( + ServiceManager.getServiceOrThrow(serviceName)); + }}); + } + + /** + * Similar to {@link #registerStaticService(String, Class, StaticServiceProducerWithBinder)}, + * but used for a "service wrapper" that doesn't take a service binder in its constructor. + * + * @hide + */ + @SystemApi + public static void registerStaticService( + @NonNull String serviceName, @NonNull Class serviceWrapperClass, + @NonNull StaticServiceProducerWithoutBinder serviceProducer) { + ensureInitializing("registerStaticService"); + Preconditions.checkStringNotEmpty(serviceName); + Objects.requireNonNull(serviceWrapperClass); + Objects.requireNonNull(serviceProducer); + + registerService(serviceName, serviceWrapperClass, + new StaticServiceFetcher() { + @Override + public TServiceClass createService() { + return serviceProducer.createService(); + }}); + } + + /** + * Used by apex modules to register a "service wrapper" that is tied to a specific + * {@link Context}. + * + *

This can only be called from the methods called by the static initializer of + * {@link SystemServiceRegistry}. (Otherwise it throws a {@link IllegalStateException}.) + * + * @param serviceName the name of the binder object, such as + * {@link Context#JOB_SCHEDULER_SERVICE}. + * @param serviceWrapperClass the wrapper class, such as the class of + * {@link android.app.job.JobScheduler}. + * @param serviceProducer lambda that takes the service binder object with the name + * {@code serviceName}, a {@link Context} and returns an actual service wrapper instance. + * + * @hide + */ + @SystemApi + public static void registerContextAwareService( + @NonNull String serviceName, @NonNull Class serviceWrapperClass, + @NonNull ContextAwareServiceProducerWithBinder serviceProducer) { + ensureInitializing("registerContextAwareService"); + Preconditions.checkStringNotEmpty(serviceName); + Objects.requireNonNull(serviceWrapperClass); + Objects.requireNonNull(serviceProducer); + + registerService(serviceName, serviceWrapperClass, + new CachedServiceFetcher() { + @Override + public TServiceClass createService(ContextImpl ctx) + throws ServiceNotFoundException { + return serviceProducer.createService( + ctx.getOuterContext(), + ServiceManager.getServiceOrThrow(serviceName)); + }}); + } + + + /** + * Similar to {@link #registerContextAwareService(String, Class, + * ContextAwareServiceProducerWithBinder)}, + * but used for a "service wrapper" that doesn't take a service binder in its constructor. + * + * @hide + */ + @SystemApi + public static void registerContextAwareService( + @NonNull String serviceName, @NonNull Class serviceWrapperClass, + @NonNull ContextAwareServiceProducerWithoutBinder serviceProducer) { + ensureInitializing("registerContextAwareService"); + Preconditions.checkStringNotEmpty(serviceName); + Objects.requireNonNull(serviceWrapperClass); + Objects.requireNonNull(serviceProducer); + + registerService(serviceName, serviceWrapperClass, + new CachedServiceFetcher() { + @Override + public TServiceClass createService(ContextImpl ctx) { + return serviceProducer.createService(ctx.getOuterContext()); + }}); + } + + /** + * Base interface for classes that fetch services. + * These objects must only be created during static initialization. + */ + static abstract interface ServiceFetcher { + T getService(ContextImpl ctx); + } + + /** + * Override this class when the system service constructor needs a + * ContextImpl and should be cached and retained by that context. + */ + static abstract class CachedServiceFetcher implements ServiceFetcher { + private final int mCacheIndex; + + CachedServiceFetcher() { + // Note this class must be instantiated only by the static initializer of the + // outer class (SystemServiceRegistry), which already does the synchronization, + // so bare access to sServiceCacheSize is okay here. + mCacheIndex = sServiceCacheSize++; + } + + @Override + @SuppressWarnings("unchecked") + public final T getService(ContextImpl ctx) { + final Object[] cache = ctx.mServiceCache; + final int[] gates = ctx.mServiceInitializationStateArray; + boolean interrupted = false; + + T ret = null; + + for (;;) { + boolean doInitialize = false; + synchronized (cache) { + // Return it if we already have a cached instance. + T service = (T) cache[mCacheIndex]; + if (service != null) { + ret = service; + break; // exit the for (;;) + } + + // If we get here, there's no cached instance. + + // Grr... if gate is STATE_READY, then this means we initialized the service + // once but someone cleared it. + // We start over from STATE_UNINITIALIZED. + // Similarly, if the previous attempt returned null, we'll retry again. + if (gates[mCacheIndex] == ContextImpl.STATE_READY + || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) { + gates[mCacheIndex] = ContextImpl.STATE_UNINITIALIZED; + } + + // It's possible for multiple threads to get here at the same time, so + // use the "gate" to make sure only the first thread will call createService(). + + // At this point, the gate must be either UNINITIALIZED or INITIALIZING. + if (gates[mCacheIndex] == ContextImpl.STATE_UNINITIALIZED) { + doInitialize = true; + gates[mCacheIndex] = ContextImpl.STATE_INITIALIZING; + } + } + + if (doInitialize) { + // Only the first thread gets here. + + T service = null; + @ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND; + try { + // This thread is the first one to get here. Instantiate the service + // *without* the cache lock held. + service = createService(ctx); + newState = ContextImpl.STATE_READY; + + } catch (ServiceNotFoundException e) { + onServiceNotFound(e); + + } finally { + synchronized (cache) { + cache[mCacheIndex] = service; + gates[mCacheIndex] = newState; + cache.notifyAll(); + } + } + ret = service; + break; // exit the for (;;) + } + // The other threads will wait for the first thread to call notifyAll(), + // and go back to the top and retry. + synchronized (cache) { + // Repeat until the state becomes STATE_READY or STATE_NOT_FOUND. + // We can't respond to interrupts here; just like we can't in the "doInitialize" + // path, so we remember the interrupt state here and re-interrupt later. + while (gates[mCacheIndex] < ContextImpl.STATE_READY) { + try { + // Clear the interrupt state. + interrupted |= Thread.interrupted(); + cache.wait(); + } catch (InterruptedException e) { + // This shouldn't normally happen, but if someone interrupts the + // thread, it will. + Slog.w(TAG, "getService() interrupted"); + interrupted = true; + } + } + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } + return ret; + } + + public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException; + } + + /** + * Override this class when the system service does not need a ContextImpl + * and should be cached and retained process-wide. + */ + static abstract class StaticServiceFetcher implements ServiceFetcher { + private T mCachedInstance; + + @Override + public final T getService(ContextImpl ctx) { + synchronized (StaticServiceFetcher.this) { + if (mCachedInstance == null) { + try { + mCachedInstance = createService(); + } catch (ServiceNotFoundException e) { + onServiceNotFound(e); + } + } + return mCachedInstance; + } + } + + public abstract T createService() throws ServiceNotFoundException; + } + + /** + * Like StaticServiceFetcher, creates only one instance of the service per application, but when + * creating the service for the first time, passes it the application context of the creating + * application. + * + * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the + * case where multiple application components each have their own ConnectivityManager object. + */ + static abstract class StaticApplicationContextServiceFetcher implements ServiceFetcher { + private T mCachedInstance; + + @Override + public final T getService(ContextImpl ctx) { + synchronized (StaticApplicationContextServiceFetcher.this) { + if (mCachedInstance == null) { + Context appContext = ctx.getApplicationContext(); + // If the application context is null, we're either in the system process or + // it's the application context very early in app initialization. In both these + // cases, the passed-in ContextImpl will not be freed, so it's safe to pass it + // to the service. http://b/27532714 . + try { + mCachedInstance = createService(appContext != null ? appContext : ctx); + } catch (ServiceNotFoundException e) { + onServiceNotFound(e); + } + } + return mCachedInstance; + } + } + + public abstract T createService(Context applicationContext) throws ServiceNotFoundException; + } + + /** @hide */ + public static void onServiceNotFound(ServiceNotFoundException e) { + // We're mostly interested in tracking down long-lived core system + // components that might stumble if they obtain bad references; just + // emit a tidy log message for normal apps + if (android.os.Process.myUid() < android.os.Process.FIRST_APPLICATION_UID) { + Log.wtf(TAG, e.getMessage(), e); + } else { + Log.w(TAG, e.getMessage()); + } + } +} diff --git a/code/chapter-08/apkjiagu.7z b/code/chapter-08/apkjiagu.7z new file mode 100644 index 0000000..1401204 Binary files /dev/null and b/code/chapter-08/apkjiagu.7z differ diff --git a/code/chapter-09/PineDemo.zip b/code/chapter-09/PineDemo.zip new file mode 100644 index 0000000..64a3bf1 Binary files /dev/null and b/code/chapter-09/PineDemo.zip differ diff --git a/code/chapter-12/NativeDemo.zip b/code/chapter-12/NativeDemo.zip new file mode 100644 index 0000000..8406ccb Binary files /dev/null and b/code/chapter-12/NativeDemo.zip differ diff --git a/code/chapter-12/backtrace/Android.mk b/code/chapter-12/backtrace/Android.mk new file mode 100644 index 0000000..089b1c4 --- /dev/null +++ b/code/chapter-12/backtrace/Android.mk @@ -0,0 +1,45 @@ + +#-------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := libxdl +LOCAL_SRC_FILES_arm := libxdl.so +LOCAL_SRC_FILES_arm64 := libxdl_arm64.so +LOCAL_MODULE_TARGET_ARCHS:= arm arm64 +LOCAL_MULTILIB := both +LOCAL_MODULE_SUFFIX := .so +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES := liblog + +include $(BUILD_PREBUILT) + +#-------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := libxunwind +LOCAL_SRC_FILES_arm := libxunwind.so +LOCAL_SRC_FILES_arm64 := libxunwind_arm64.so +LOCAL_MODULE_TARGET_ARCHS:= arm arm64 +LOCAL_MULTILIB := both +LOCAL_MODULE_SUFFIX := .so +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES := liblog libxdl + +include $(BUILD_PREBUILT) + +#-------------------------------- +include $(CLEAR_VARS) + +LOCAL_MODULE := libkbacktrace +LOCAL_SRC_FILES_arm := libkbacktrace_32.so +LOCAL_SRC_FILES_arm64 := libkbacktrace_64.so +LOCAL_MODULE_TARGET_ARCHS:= arm arm64 +LOCAL_MULTILIB := both +LOCAL_MODULE_SUFFIX := .so +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES := liblog libxunwind + +include $(BUILD_PREBUILT) \ No newline at end of file diff --git a/code/chapter-12/backtrace/libkbacktrace_32.so b/code/chapter-12/backtrace/libkbacktrace_32.so new file mode 100644 index 0000000..f6c0857 Binary files /dev/null and b/code/chapter-12/backtrace/libkbacktrace_32.so differ diff --git a/code/chapter-12/backtrace/libkbacktrace_64.so b/code/chapter-12/backtrace/libkbacktrace_64.so new file mode 100644 index 0000000..0ccf6d6 Binary files /dev/null and b/code/chapter-12/backtrace/libkbacktrace_64.so differ diff --git a/code/chapter-12/backtrace/libxdl.so b/code/chapter-12/backtrace/libxdl.so new file mode 100644 index 0000000..c00981b Binary files /dev/null and b/code/chapter-12/backtrace/libxdl.so differ diff --git a/code/chapter-12/backtrace/libxdl_arm64.so b/code/chapter-12/backtrace/libxdl_arm64.so new file mode 100644 index 0000000..d3ff58a Binary files /dev/null and b/code/chapter-12/backtrace/libxdl_arm64.so differ diff --git a/code/chapter-12/backtrace/libxunwind.so b/code/chapter-12/backtrace/libxunwind.so new file mode 100644 index 0000000..df1ba81 Binary files /dev/null and b/code/chapter-12/backtrace/libxunwind.so differ diff --git a/code/chapter-12/backtrace/libxunwind_arm64.so b/code/chapter-12/backtrace/libxunwind_arm64.so new file mode 100644 index 0000000..9b1b84a Binary files /dev/null and b/code/chapter-12/backtrace/libxunwind_arm64.so differ diff --git a/code/chapter-12/backtraceDemo.zip b/code/chapter-12/backtraceDemo.zip new file mode 100644 index 0000000..fbed604 Binary files /dev/null and b/code/chapter-12/backtraceDemo.zip differ diff --git a/code/chapter-12/dalvik_system_DexFile.cc b/code/chapter-12/dalvik_system_DexFile.cc new file mode 100644 index 0000000..5f27a23 --- /dev/null +++ b/code/chapter-12/dalvik_system_DexFile.cc @@ -0,0 +1,1050 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dalvik_system_DexFile.h" + +#include + +#include "android-base/file.h" +#include "android-base/stringprintf.h" + +#include "base/casts.h" +#include "base/compiler_filter.h" +#include "base/file_utils.h" +#include "base/hiddenapi_domain.h" +#include "base/logging.h" +#include "base/os.h" +#include "base/stl_util.h" +#include "base/utils.h" +#include "base/zip_archive.h" +#include "class_linker.h" +#include "class_loader_context.h" +#include "common_throws.h" +#include "dex/art_dex_file_loader.h" +#include "dex/descriptors_names.h" +#include "dex/dex_file-inl.h" +#include "dex/dex_file_loader.h" +#include "gc/space/image_space.h" +#include "handle_scope-inl.h" +#include "jit/debugger_interface.h" +#include "jni/jni_internal.h" +#include "mirror/class_loader.h" +#include "mirror/object-inl.h" +#include "mirror/string.h" +#include "native_util.h" +#include "nativehelper/jni_macros.h" +#include "nativehelper/scoped_local_ref.h" +#include "nativehelper/scoped_utf_chars.h" +#include "oat_file.h" +#include "oat_file_assistant.h" +#include "oat_file_manager.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "well_known_classes.h" +#include +#include "utils/Log.h" +namespace art { + + +extern "C" void InvokeMethod(ArtMethod* artmethod,jboolean isDeep); +extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod); +// extern "C" void dumpDexOver(); + +using android::base::StringPrintf; + +static bool ConvertJavaArrayToDexFiles( + JNIEnv* env, + jobject arrayObject, + /*out*/ std::vector& dex_files, + /*out*/ const OatFile*& oat_file) { + jarray array = reinterpret_cast(arrayObject); + + jsize array_size = env->GetArrayLength(array); + if (env->ExceptionCheck() == JNI_TRUE) { + return false; + } + + // TODO: Optimize. On 32bit we can use an int array. + jboolean is_long_data_copied; + jlong* long_data = env->GetLongArrayElements(reinterpret_cast(array), + &is_long_data_copied); + if (env->ExceptionCheck() == JNI_TRUE) { + return false; + } + + oat_file = reinterpret_cast64(long_data[kOatFileIndex]); + dex_files.reserve(array_size - 1); + for (jsize i = kDexFileIndexStart; i < array_size; ++i) { + dex_files.push_back(reinterpret_cast64(long_data[i])); + } + + env->ReleaseLongArrayElements(reinterpret_cast(array), long_data, JNI_ABORT); + return env->ExceptionCheck() != JNI_TRUE; +} + +static jlongArray ConvertDexFilesToJavaArray(JNIEnv* env, + const OatFile* oat_file, + std::vector>& vec) { + // Add one for the oat file. + jlongArray long_array = env->NewLongArray(static_cast(kDexFileIndexStart + vec.size())); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + + jboolean is_long_data_copied; + jlong* long_data = env->GetLongArrayElements(long_array, &is_long_data_copied); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + + long_data[kOatFileIndex] = reinterpret_cast64(oat_file); + for (size_t i = 0; i < vec.size(); ++i) { + long_data[kDexFileIndexStart + i] = reinterpret_cast64(vec[i].get()); + } + + env->ReleaseLongArrayElements(long_array, long_data, 0); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + + // Now release all the unique_ptrs. + for (auto& dex_file : vec) { + dex_file.release(); // NOLINT + } + + return long_array; +} + +// A smart pointer that provides read-only access to a Java string's UTF chars. +// Unlike libcore's NullableScopedUtfChars, this will *not* throw NullPointerException if +// passed a null jstring. The correct idiom is: +// +// NullableScopedUtfChars name(env, javaName); +// if (env->ExceptionCheck()) { +// return null; +// } +// // ... use name.c_str() +// +// TODO: rewrite to get rid of this, or change ScopedUtfChars to offer this option. +class NullableScopedUtfChars { + public: + NullableScopedUtfChars(JNIEnv* env, jstring s) : mEnv(env), mString(s) { + mUtfChars = (s != nullptr) ? env->GetStringUTFChars(s, nullptr) : nullptr; + } + + ~NullableScopedUtfChars() { + if (mUtfChars) { + mEnv->ReleaseStringUTFChars(mString, mUtfChars); + } + } + + const char* c_str() const { + return mUtfChars; + } + + size_t size() const { + return strlen(mUtfChars); + } + + // Element access. + const char& operator[](size_t n) const { + return mUtfChars[n]; + } + + private: + JNIEnv* mEnv; + jstring mString; + const char* mUtfChars; + + // Disallow copy and assignment. + NullableScopedUtfChars(const NullableScopedUtfChars&); + void operator=(const NullableScopedUtfChars&); +}; + +static jobject CreateCookieFromOatFileManagerResult( + JNIEnv* env, + std::vector>& dex_files, + const OatFile* oat_file, + const std::vector& error_msgs) { + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + if (dex_files.empty()) { + ScopedObjectAccess soa(env); + CHECK(!error_msgs.empty()); + // The most important message is at the end. So set up nesting by going forward, which will + // wrap the existing exception as a cause for the following one. + auto it = error_msgs.begin(); + auto itEnd = error_msgs.end(); + for ( ; it != itEnd; ++it) { + ThrowWrappedIOException("%s", it->c_str()); + } + return nullptr; + } + + jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files); + if (array == nullptr) { + ScopedObjectAccess soa(env); + for (auto& dex_file : dex_files) { + if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) { + dex_file.release(); // NOLINT + } + } + } + return array; +} + +static MemMap AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) { + if (end <= start) { + ScopedObjectAccess soa(env); + ThrowWrappedIOException("Bad range"); + return MemMap::Invalid(); + } + + std::string error_message; + size_t length = static_cast(end - start); + MemMap dex_mem_map = MemMap::MapAnonymous("DEX data", + length, + PROT_READ | PROT_WRITE, + /*low_4gb=*/ false, + &error_message); + if (!dex_mem_map.IsValid()) { + ScopedObjectAccess soa(env); + ThrowWrappedIOException("%s", error_message.c_str()); + return MemMap::Invalid(); + } + return dex_mem_map; +} + +struct ScopedIntArrayAccessor { + public: + ScopedIntArrayAccessor(JNIEnv* env, jintArray arr) : env_(env), array_(arr) { + elements_ = env_->GetIntArrayElements(array_, /* isCopy= */ nullptr); + CHECK(elements_ != nullptr); + } + + ~ScopedIntArrayAccessor() { + env_->ReleaseIntArrayElements(array_, elements_, JNI_ABORT); + } + + jint Get(jsize index) const { return elements_[index]; } + + private: + JNIEnv* env_; + jintArray array_; + jint* elements_; +}; + +static jobject DexFile_openInMemoryDexFilesNative(JNIEnv* env, + jclass, + jobjectArray buffers, + jobjectArray arrays, + jintArray jstarts, + jintArray jends, + jobject class_loader, + jobjectArray dex_elements) { + jsize buffers_length = env->GetArrayLength(buffers); + CHECK_EQ(buffers_length, env->GetArrayLength(arrays)); + CHECK_EQ(buffers_length, env->GetArrayLength(jstarts)); + CHECK_EQ(buffers_length, env->GetArrayLength(jends)); + + ScopedIntArrayAccessor starts(env, jstarts); + ScopedIntArrayAccessor ends(env, jends); + + // Allocate memory for dex files and copy data from ByteBuffers. + std::vector dex_mem_maps; + dex_mem_maps.reserve(buffers_length); + for (jsize i = 0; i < buffers_length; ++i) { + jobject buffer = env->GetObjectArrayElement(buffers, i); + jbyteArray array = reinterpret_cast(env->GetObjectArrayElement(arrays, i)); + jint start = starts.Get(i); + jint end = ends.Get(i); + + MemMap dex_data = AllocateDexMemoryMap(env, start, end); + if (!dex_data.IsValid()) { + DCHECK(Thread::Current()->IsExceptionPending()); + return nullptr; + } + + if (array == nullptr) { + // Direct ByteBuffer + uint8_t* base_address = reinterpret_cast(env->GetDirectBufferAddress(buffer)); + if (base_address == nullptr) { + ScopedObjectAccess soa(env); + ThrowWrappedIOException("dexFileBuffer not direct"); + return nullptr; + } + size_t length = static_cast(end - start); + memcpy(dex_data.Begin(), base_address + start, length); + } else { + // ByteBuffer backed by a byte array + jbyte* destination = reinterpret_cast(dex_data.Begin()); + env->GetByteArrayRegion(array, start, end - start, destination); + } + + dex_mem_maps.push_back(std::move(dex_data)); + } + + // Hand MemMaps over to OatFileManager to open the dex files and potentially + // create a backing OatFile instance from an anonymous vdex. + std::vector error_msgs; + const OatFile* oat_file = nullptr; + std::vector> dex_files = + Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(std::move(dex_mem_maps), + class_loader, + dex_elements, + /*out*/ &oat_file, + /*out*/ &error_msgs); + return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs); +} + +// TODO(calin): clean up the unused parameters (here and in libcore). +static jobject DexFile_openDexFileNative(JNIEnv* env, + jclass, + jstring javaSourceName, + jstring javaOutputName ATTRIBUTE_UNUSED, + jint flags ATTRIBUTE_UNUSED, + jobject class_loader, + jobjectArray dex_elements) { + ScopedUtfChars sourceName(env, javaSourceName); + if (sourceName.c_str() == nullptr) { + return nullptr; + } + + std::vector error_msgs; + const OatFile* oat_file = nullptr; + std::vector> dex_files = + Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(), + class_loader, + dex_elements, + /*out*/ &oat_file, + /*out*/ &error_msgs); + return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs); +} + +static void DexFile_verifyInBackgroundNative(JNIEnv* env, + jclass, + jobject cookie, + jobject class_loader) { + CHECK(cookie != nullptr); + CHECK(class_loader != nullptr); + + // Extract list of dex files from the cookie. + std::vector dex_files; + const OatFile* oat_file; + if (!ConvertJavaArrayToDexFiles(env, cookie, dex_files, oat_file)) { + Thread::Current()->AssertPendingException(); + return; + } + CHECK(oat_file == nullptr) << "Called verifyInBackground on a dex file backed by oat"; + + // Hand over to OatFileManager to spawn a verification thread. + Runtime::Current()->GetOatFileManager().RunBackgroundVerification( + dex_files, + class_loader); +} + +static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) { + std::vector dex_files; + const OatFile* oat_file; + if (!ConvertJavaArrayToDexFiles(env, cookie, dex_files, oat_file)) { + Thread::Current()->AssertPendingException(); + return JNI_FALSE; + } + Runtime* const runtime = Runtime::Current(); + bool all_deleted = true; + // We need to clear the caches since they may contain pointers to the dex instructions. + // Different dex file can be loaded at the same memory location later by chance. + Thread::ClearAllInterpreterCaches(); + { + ScopedObjectAccess soa(env); + ObjPtr dex_files_object = soa.Decode(cookie); + ObjPtr long_dex_files = dex_files_object->AsLongArray(); + // Delete dex files associated with this dalvik.system.DexFile since there should not be running + // code using it. dex_files is a vector due to multidex. + ClassLinker* const class_linker = runtime->GetClassLinker(); + int32_t i = kDexFileIndexStart; // Oat file is at index 0. + for (const DexFile* dex_file : dex_files) { + if (dex_file != nullptr) { + RemoveNativeDebugInfoForDex(soa.Self(), dex_file); + // Only delete the dex file if the dex cache is not found to prevent runtime crashes + // if there are calls to DexFile.close while the ART DexFile is still in use. + if (!class_linker->IsDexFileRegistered(soa.Self(), *dex_file)) { + // Clear the element in the array so that we can call close again. + long_dex_files->Set(i, 0); + delete dex_file; + } else { + all_deleted = false; + } + } + ++i; + } + } + + // oat_file can be null if we are running without dex2oat. + if (all_deleted && oat_file != nullptr) { + // If all of the dex files are no longer in use we can unmap the corresponding oat file. + VLOG(class_linker) << "Unregistering " << oat_file; + runtime->GetOatFileManager().UnRegisterAndDeleteOatFile(oat_file); + } + return all_deleted ? JNI_TRUE : JNI_FALSE; +} + +static jclass DexFile_defineClassNative(JNIEnv* env, + jclass, + jstring javaName, + jobject javaLoader, + jobject cookie, + jobject dexFile) { + std::vector dex_files; + const OatFile* oat_file; + if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) { + VLOG(class_linker) << "Failed to find dex_file"; + DCHECK(env->ExceptionCheck()); + return nullptr; + } + + ScopedUtfChars class_name(env, javaName); + if (class_name.c_str() == nullptr) { + VLOG(class_linker) << "Failed to find class_name"; + return nullptr; + } + const std::string descriptor(DotToDescriptor(class_name.c_str())); + const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str())); + for (auto& dex_file : dex_files) { + const dex::ClassDef* dex_class_def = + OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash); + if (dex_class_def != nullptr) { + ScopedObjectAccess soa(env); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + StackHandleScope<1> hs(soa.Self()); + Handle class_loader( + hs.NewHandle(soa.Decode(javaLoader))); + ObjPtr dex_cache = + class_linker->RegisterDexFile(*dex_file, class_loader.Get()); + if (dex_cache == nullptr) { + // OOME or InternalError (dexFile already registered with a different class loader). + soa.Self()->AssertPendingException(); + return nullptr; + } + ObjPtr result = class_linker->DefineClass(soa.Self(), + descriptor.c_str(), + hash, + class_loader, + *dex_file, + *dex_class_def); + // Add the used dex file. This only required for the DexFile.loadClass API since normal + // class loaders already keep their dex files live. + class_linker->InsertDexFileInToClassLoader(soa.Decode(dexFile), + class_loader.Get()); + if (result != nullptr) { + VLOG(class_linker) << "DexFile_defineClassNative returning " << result + << " for " << class_name.c_str(); + return soa.AddLocalReference(result); + } + } + } + VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str(); + return nullptr; +} + +// Needed as a compare functor for sets of const char +struct CharPointerComparator { + bool operator()(const char *str1, const char *str2) const { + return strcmp(str1, str2) < 0; + } +}; + +// Note: this can be an expensive call, as we sort out duplicates in MultiDex files. +static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie) { + const OatFile* oat_file = nullptr; + std::vector dex_files; + if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) { + DCHECK(env->ExceptionCheck()); + return nullptr; + } + + // Push all class descriptors into a set. Use set instead of unordered_set as we want to + // retrieve all in the end. + std::set descriptors; + for (auto& dex_file : dex_files) { + for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const dex::ClassDef& class_def = dex_file->GetClassDef(i); + const char* descriptor = dex_file->GetClassDescriptor(class_def); + descriptors.insert(descriptor); + } + } + + // Now create output array and copy the set into it. + jobjectArray result = env->NewObjectArray(descriptors.size(), + WellKnownClasses::java_lang_String, + nullptr); + if (result != nullptr) { + auto it = descriptors.begin(); + auto it_end = descriptors.end(); + jsize i = 0; + for (; it != it_end; it++, ++i) { + std::string descriptor(DescriptorToDot(*it)); + ScopedLocalRef jdescriptor(env, env->NewStringUTF(descriptor.c_str())); + if (jdescriptor.get() == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(result, i, jdescriptor.get()); + } + } + return result; +} + +static jint GetDexOptNeeded(JNIEnv* env, + const char* filename, + const char* instruction_set, + const char* compiler_filter_name, + const char* class_loader_context, + bool profile_changed, + bool downgrade) { + if ((filename == nullptr) || !OS::FileExists(filename)) { + LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist"; + ScopedLocalRef fnfe(env, env->FindClass("java/io/FileNotFoundException")); + const char* message = (filename == nullptr) ? "" : filename; + env->ThrowNew(fnfe.get(), message); + return -1; + } + + const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set); + if (target_instruction_set == InstructionSet::kNone) { + ScopedLocalRef iae(env, env->FindClass("java/lang/IllegalArgumentException")); + std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set)); + env->ThrowNew(iae.get(), message.c_str()); + return -1; + } + + CompilerFilter::Filter filter; + if (!CompilerFilter::ParseCompilerFilter(compiler_filter_name, &filter)) { + ScopedLocalRef iae(env, env->FindClass("java/lang/IllegalArgumentException")); + std::string message(StringPrintf("Compiler filter %s is invalid.", compiler_filter_name)); + env->ThrowNew(iae.get(), message.c_str()); + return -1; + } + + std::unique_ptr context = nullptr; + if (class_loader_context != nullptr) { + context = ClassLoaderContext::Create(class_loader_context); + + if (context == nullptr) { + ScopedLocalRef iae(env, env->FindClass("java/lang/IllegalArgumentException")); + std::string message(StringPrintf("Class loader context '%s' is invalid.", + class_loader_context)); + env->ThrowNew(iae.get(), message.c_str()); + return -1; + } + std::vector context_fds; + context->OpenDexFiles(android::base::Dirname(filename), + context_fds, + /*only_read_checksums*/ true); + } + + // TODO: Verify the dex location is well formed, and throw an IOException if + // not? + + OatFileAssistant oat_file_assistant(filename, + target_instruction_set, + context.get(), + /* load_executable= */ false); + + // Always treat elements of the bootclasspath as up-to-date. + if (oat_file_assistant.IsInBootClassPath()) { + return OatFileAssistant::kNoDexOptNeeded; + } + + return oat_file_assistant.GetDexOptNeeded(filter, + profile_changed, + downgrade); +} + +static jstring DexFile_getDexFileStatus(JNIEnv* env, + jclass, + jstring javaFilename, + jstring javaInstructionSet) { + ScopedUtfChars filename(env, javaFilename); + if (env->ExceptionCheck()) { + return nullptr; + } + + ScopedUtfChars instruction_set(env, javaInstructionSet); + if (env->ExceptionCheck()) { + return nullptr; + } + + const InstructionSet target_instruction_set = GetInstructionSetFromString( + instruction_set.c_str()); + if (target_instruction_set == InstructionSet::kNone) { + ScopedLocalRef iae(env, env->FindClass("java/lang/IllegalArgumentException")); + std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str())); + env->ThrowNew(iae.get(), message.c_str()); + return nullptr; + } + + OatFileAssistant oat_file_assistant(filename.c_str(), + target_instruction_set, + /* context= */ nullptr, + /* load_executable= */ false); + return env->NewStringUTF(oat_file_assistant.GetStatusDump().c_str()); +} + +// Return an array specifying the optimization status of the given file. +// The array specification is [compiler_filter, compiler_reason]. +static jobjectArray DexFile_getDexFileOptimizationStatus(JNIEnv* env, + jclass, + jstring javaFilename, + jstring javaInstructionSet) { + ScopedUtfChars filename(env, javaFilename); + if (env->ExceptionCheck()) { + return nullptr; + } + + ScopedUtfChars instruction_set(env, javaInstructionSet); + if (env->ExceptionCheck()) { + return nullptr; + } + + const InstructionSet target_instruction_set = GetInstructionSetFromString( + instruction_set.c_str()); + if (target_instruction_set == InstructionSet::kNone) { + ScopedLocalRef iae(env, env->FindClass("java/lang/IllegalArgumentException")); + std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str())); + env->ThrowNew(iae.get(), message.c_str()); + return nullptr; + } + + std::string compilation_filter; + std::string compilation_reason; + OatFileAssistant::GetOptimizationStatus( + filename.c_str(), target_instruction_set, &compilation_filter, &compilation_reason); + + ScopedLocalRef j_compilation_filter(env, env->NewStringUTF(compilation_filter.c_str())); + if (j_compilation_filter.get() == nullptr) { + return nullptr; + } + ScopedLocalRef j_compilation_reason(env, env->NewStringUTF(compilation_reason.c_str())); + if (j_compilation_reason.get() == nullptr) { + return nullptr; + } + + // Now create output array and copy the set into it. + jobjectArray result = env->NewObjectArray(2, + WellKnownClasses::java_lang_String, + nullptr); + env->SetObjectArrayElement(result, 0, j_compilation_filter.get()); + env->SetObjectArrayElement(result, 1, j_compilation_reason.get()); + + return result; +} + +static jint DexFile_getDexOptNeeded(JNIEnv* env, + jclass, + jstring javaFilename, + jstring javaInstructionSet, + jstring javaTargetCompilerFilter, + jstring javaClassLoaderContext, + jboolean newProfile, + jboolean downgrade) { + ScopedUtfChars filename(env, javaFilename); + if (env->ExceptionCheck()) { + return -1; + } + + ScopedUtfChars instruction_set(env, javaInstructionSet); + if (env->ExceptionCheck()) { + return -1; + } + + ScopedUtfChars target_compiler_filter(env, javaTargetCompilerFilter); + if (env->ExceptionCheck()) { + return -1; + } + + NullableScopedUtfChars class_loader_context(env, javaClassLoaderContext); + if (env->ExceptionCheck()) { + return -1; + } + + return GetDexOptNeeded(env, + filename.c_str(), + instruction_set.c_str(), + target_compiler_filter.c_str(), + class_loader_context.c_str(), + newProfile == JNI_TRUE, + downgrade == JNI_TRUE); +} + +// public API +static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) { + ScopedUtfChars filename_utf(env, javaFilename); + if (env->ExceptionCheck()) { + return JNI_FALSE; + } + + const char* filename = filename_utf.c_str(); + if ((filename == nullptr) || !OS::FileExists(filename)) { + LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename << "' does not exist"; + ScopedLocalRef fnfe(env, env->FindClass("java/io/FileNotFoundException")); + const char* message = (filename == nullptr) ? "" : filename; + env->ThrowNew(fnfe.get(), message); + return JNI_FALSE; + } + + OatFileAssistant oat_file_assistant(filename, + kRuntimeISA, + /* context= */ nullptr, + /* load_executable= */ false); + return oat_file_assistant.IsUpToDate() ? JNI_FALSE : JNI_TRUE; +} + +static jboolean DexFile_isValidCompilerFilter(JNIEnv* env, + jclass javeDexFileClass ATTRIBUTE_UNUSED, + jstring javaCompilerFilter) { + ScopedUtfChars compiler_filter(env, javaCompilerFilter); + if (env->ExceptionCheck()) { + return -1; + } + + CompilerFilter::Filter filter; + return CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter) + ? JNI_TRUE : JNI_FALSE; +} + +static jboolean DexFile_isProfileGuidedCompilerFilter(JNIEnv* env, + jclass javeDexFileClass ATTRIBUTE_UNUSED, + jstring javaCompilerFilter) { + ScopedUtfChars compiler_filter(env, javaCompilerFilter); + if (env->ExceptionCheck()) { + return -1; + } + + CompilerFilter::Filter filter; + if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) { + return JNI_FALSE; + } + return CompilerFilter::DependsOnProfile(filter) ? JNI_TRUE : JNI_FALSE; +} + +static jstring DexFile_getNonProfileGuidedCompilerFilter(JNIEnv* env, + jclass javeDexFileClass ATTRIBUTE_UNUSED, + jstring javaCompilerFilter) { + ScopedUtfChars compiler_filter(env, javaCompilerFilter); + if (env->ExceptionCheck()) { + return nullptr; + } + + CompilerFilter::Filter filter; + if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) { + return javaCompilerFilter; + } + + CompilerFilter::Filter new_filter = CompilerFilter::GetNonProfileDependentFilterFrom(filter); + + // Filter stayed the same, return input. + if (filter == new_filter) { + return javaCompilerFilter; + } + + // Create a new string object and return. + std::string new_filter_str = CompilerFilter::NameOfFilter(new_filter); + return env->NewStringUTF(new_filter_str.c_str()); +} + +static jstring DexFile_getSafeModeCompilerFilter(JNIEnv* env, + jclass javeDexFileClass ATTRIBUTE_UNUSED, + jstring javaCompilerFilter) { + ScopedUtfChars compiler_filter(env, javaCompilerFilter); + if (env->ExceptionCheck()) { + return nullptr; + } + + CompilerFilter::Filter filter; + if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) { + return javaCompilerFilter; + } + + CompilerFilter::Filter new_filter = CompilerFilter::GetSafeModeFilterFrom(filter); + + // Filter stayed the same, return input. + if (filter == new_filter) { + return javaCompilerFilter; + } + + // Create a new string object and return. + std::string new_filter_str = CompilerFilter::NameOfFilter(new_filter); + return env->NewStringUTF(new_filter_str.c_str()); +} + +static jboolean DexFile_isBackedByOatFile(JNIEnv* env, jclass, jobject cookie) { + const OatFile* oat_file = nullptr; + std::vector dex_files; + if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) { + DCHECK(env->ExceptionCheck()); + return false; + } + return oat_file != nullptr; +} + +static jobjectArray DexFile_getDexFileOutputPaths(JNIEnv* env, + jclass, + jstring javaFilename, + jstring javaInstructionSet) { + ScopedUtfChars filename(env, javaFilename); + if (env->ExceptionCheck()) { + return nullptr; + } + + ScopedUtfChars instruction_set(env, javaInstructionSet); + if (env->ExceptionCheck()) { + return nullptr; + } + + const InstructionSet target_instruction_set = GetInstructionSetFromString( + instruction_set.c_str()); + if (target_instruction_set == InstructionSet::kNone) { + ScopedLocalRef iae(env, env->FindClass("java/lang/IllegalArgumentException")); + std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str())); + env->ThrowNew(iae.get(), message.c_str()); + return nullptr; + } + + std::string oat_filename; + std::string vdex_filename; + // Check if the file is in the boot classpath by looking at image spaces which + // have oat files. + for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) { + const OatFile* oat_file = space->GetOatFile(); + if (oat_file != nullptr) { + const std::vector& oat_dex_files = oat_file->GetOatDexFiles(); + for (const OatDexFile* oat_dex_file : oat_dex_files) { + if (DexFileLoader::GetBaseLocation(oat_dex_file->GetDexFileLocation()) == + filename.c_str()) { + oat_filename = GetSystemImageFilename(oat_file->GetLocation().c_str(), + target_instruction_set); + break; + } + } + if (!oat_filename.empty()) { + break; + } + } + } + + // If we did not find a boot classpath oat file, lookup the oat file for an app. + if (oat_filename.empty()) { + OatFileAssistant oat_file_assistant(filename.c_str(), + target_instruction_set, + /* context= */ nullptr, + /* load_executable= */ false); + + std::unique_ptr best_oat_file = oat_file_assistant.GetBestOatFile(); + if (best_oat_file == nullptr) { + return nullptr; + } + + oat_filename = best_oat_file->GetLocation(); + } + vdex_filename = GetVdexFilename(oat_filename); + + ScopedLocalRef jvdexFilename(env, env->NewStringUTF(vdex_filename.c_str())); + if (jvdexFilename.get() == nullptr) { + return nullptr; + } + ScopedLocalRef joatFilename(env, env->NewStringUTF(oat_filename.c_str())); + if (joatFilename.get() == nullptr) { + return nullptr; + } + + // Now create output array and copy the set into it. + jobjectArray result = env->NewObjectArray(2, + WellKnownClasses::java_lang_String, + nullptr); + env->SetObjectArrayElement(result, 0, jvdexFilename.get()); + env->SetObjectArrayElement(result, 1, joatFilename.get()); + + return result; +} + +static jlong DexFile_getStaticSizeOfDexFile(JNIEnv* env, jclass, jobject cookie) { + const OatFile* oat_file = nullptr; + std::vector dex_files; + if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) { + DCHECK(env->ExceptionCheck()); + return 0; + } + + uint64_t file_size = 0; + for (auto& dex_file : dex_files) { + if (dex_file) { + file_size += dex_file->GetHeader().file_size_; + } + } + return static_cast(file_size); +} + +static void DexFile_setTrusted(JNIEnv* env, jclass, jobject j_cookie) { + Runtime* runtime = Runtime::Current(); + ScopedObjectAccess soa(env); + + // Currently only allow this for debuggable apps. + if (!runtime->IsJavaDebuggable()) { + ThrowSecurityException("Can't exempt class, process is not debuggable."); + return; + } + + std::vector dex_files; + const OatFile* oat_file; + if (!ConvertJavaArrayToDexFiles(env, j_cookie, dex_files, oat_file)) { + Thread::Current()->AssertPendingException(); + return; + } + + // Assign core platform domain as the dex files are allowed to access all the other domains. + for (const DexFile* dex_file : dex_files) { + const_cast(dex_file)->SetHiddenapiDomain(hiddenapi::Domain::kCorePlatform); + } +} + +//addfunction å°†avaçš„Methodè½¬æ¢æˆArtMethod。然åŽä¸»åŠ¨è°ƒç”¨ +static void DexFile_mikromInvokeMethod(JNIEnv* env, jclass,jobject method,jboolean isDeep) { + if(method!=nullptr) + { +// LOG(ERROR) << "mikrom DexFile_mikromInvokeMethod"; + jobject2ArtMethod(env, method); + ArtMethod* proxy_method = jobject2ArtMethod(env, method); + InvokeMethod(proxy_method,isDeep); + } + return; +} + +static void +DexFile_initConfig(JNIEnv* env, jobject ,jobject item) { + + Runtime* runtime=Runtime::Current(); + jclass jcInfo = env->FindClass("java/krom/PackageItem"); + jfieldID jPackageName = env->GetFieldID(jcInfo, "packageName", "Ljava/lang/String;"); + jfieldID jJniModuleName = env->GetFieldID(jcInfo, "jniModuleName", "Ljava/lang/String;"); + jfieldID jJniFuncName = env->GetFieldID(jcInfo, "jniFuncName", "Ljava/lang/String;"); + jfieldID jIsRegisterNativePrint = env->GetFieldID(jcInfo, "isRegisterNativePrint", "Z"); + jfieldID jIsJNIMethodPrint = env->GetFieldID(jcInfo, "isJNIMethodPrint", "Z"); + + PackageItem citem; + + jstring jstrPackageName = (jstring)env->GetObjectField(item, jPackageName); + const char* pPackageName = (char*)env->GetStringUTFChars(jstrPackageName, 0); + strcpy(citem.packageName, pPackageName); + + jstring jstrJniModuleName = (jstring)env->GetObjectField(item, jJniModuleName); + const char* pJniModuleName = (char*)env->GetStringUTFChars(jstrJniModuleName, 0); + strcpy(citem.jniModuleName, pJniModuleName); + + jstring jstrJniFuncName = (jstring)env->GetObjectField(item, jJniFuncName); + const char* pJniFuncName = (char*)env->GetStringUTFChars(jstrJniFuncName, 0); + strcpy(citem.jniFuncName, pJniFuncName); + + citem.isRegisterNativePrint = env->GetBooleanField(item, jIsRegisterNativePrint); + citem.isJNIMethodPrint = env->GetBooleanField(item, jIsJNIMethodPrint); + if(citem.isJNIMethodPrint){ + void* handle_xdl=NULL; + void* handle_xunwind=NULL; + void* handle_kbacktrace=NULL; + #if defined(__aarch64__) + // å½“å‰ CPU 架构为 arm64 + handle_xdl= dlopen("/system/lib64/libxdl.so",RTLD_NOW); + handle_xunwind= dlopen("/system/lib64/libxunwind.so",RTLD_NOW); + handle_kbacktrace= dlopen("/system/lib64/libkbacktrace.so",RTLD_NOW); + #else + // å½“å‰ CPU 架构为 armeabi-v7a 或更早版本 + handle_xdl= dlopen("/system/lib/libxdl.so",RTLD_NOW); + handle_xunwind= dlopen("/system/lib/libxunwind.so",RTLD_NOW); + handle_kbacktrace= dlopen("/system/lib/libkbacktrace.so",RTLD_NOW); + #endif + if(handle_kbacktrace!=nullptr){ + citem.kbacktrace= dlsym(handle_kbacktrace, "_Z10kbacktracebPKc"); + if(citem.kbacktrace==nullptr){ + ALOGD("mikrom kbacktrace is null.err:%s",dlerror()); + }else{ + ALOGD("mikrom kbacktrace:%p.",citem.kbacktrace); + } + }else{ + ALOGD("mikrom handle_kbacktrace is null.err:%s",dlerror()); + } + } + runtime->SetConfigItem(citem); +} + + + + +static JNINativeMethod gMethods[] = { + NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"), + NATIVE_METHOD(DexFile, + defineClassNative, + "(Ljava/lang/String;" + "Ljava/lang/ClassLoader;" + "Ljava/lang/Object;" + "Ldalvik/system/DexFile;" + ")Ljava/lang/Class;"), + NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"), + NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"), + NATIVE_METHOD(DexFile, getDexOptNeeded, + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"), + NATIVE_METHOD(DexFile, openDexFileNative, + "(Ljava/lang/String;" + "Ljava/lang/String;" + "I" + "Ljava/lang/ClassLoader;" + "[Ldalvik/system/DexPathList$Element;" + ")Ljava/lang/Object;"), + NATIVE_METHOD(DexFile, openInMemoryDexFilesNative, + "([Ljava/nio/ByteBuffer;" + "[[B" + "[I" + "[I" + "Ljava/lang/ClassLoader;" + "[Ldalvik/system/DexPathList$Element;" + ")Ljava/lang/Object;"), + NATIVE_METHOD(DexFile, verifyInBackgroundNative, + "(Ljava/lang/Object;" + "Ljava/lang/ClassLoader;" + ")V"), + NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"), + NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"), + NATIVE_METHOD(DexFile, + getNonProfileGuidedCompilerFilter, + "(Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(DexFile, + getSafeModeCompilerFilter, + "(Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(DexFile, isBackedByOatFile, "(Ljava/lang/Object;)Z"), + NATIVE_METHOD(DexFile, getDexFileStatus, + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(DexFile, getDexFileOutputPaths, + "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"), + NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"), + NATIVE_METHOD(DexFile, getDexFileOptimizationStatus, + "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"), + NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V"), + NATIVE_METHOD(DexFile, mikromInvokeMethod,"(Ljava/lang/Object;Z)V"), + NATIVE_METHOD(DexFile, initConfig,"(Ljava/lang/Object;)V"), +}; + +void register_dalvik_system_DexFile(JNIEnv* env) { + REGISTER_NATIVE_METHODS("dalvik/system/DexFile"); +} + +} // namespace art diff --git a/code/chapter-12/jni_internal.cc b/code/chapter-12/jni_internal.cc new file mode 100644 index 0000000..2ef8a86 --- /dev/null +++ b/code/chapter-12/jni_internal.cc @@ -0,0 +1,3444 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni_internal.h" + +#include +#include +#include +#include + +#include "art_field-inl.h" +#include "art_method-inl.h" +#include "base/allocator.h" +#include "base/atomic.h" +#include "base/casts.h" +#include "base/enums.h" +#include "base/file_utils.h" +#include "base/logging.h" // For VLOG. +#include "base/mutex.h" +#include "base/safe_map.h" +#include "base/stl_util.h" +#include "class_linker-inl.h" +#include "class_root-inl.h" +#include "dex/dex_file-inl.h" +#include "dex/utf-inl.h" +#include "fault_handler.h" +#include "hidden_api.h" +#include "gc/accounting/card_table-inl.h" +#include "gc_root.h" +#include "indirect_reference_table-inl.h" +#include "interpreter/interpreter.h" +#include "java_vm_ext.h" +#include "jni_env_ext.h" +#include "jvalue-inl.h" +#include "mirror/class-alloc-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache-inl.h" +#include "mirror/field.h" +#include "mirror/method.h" +#include "mirror/object-inl.h" +#include "mirror/object_array-alloc-inl.h" +#include "mirror/object_array-inl.h" +#include "mirror/string-alloc-inl.h" +#include "mirror/string-inl.h" +#include "mirror/throwable.h" +#include "nativehelper/scoped_local_ref.h" +#include "parsed_options.h" +#include "reflection.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread.h" +#include "well_known_classes.h" +#include "utils/Log.h" +namespace art { + +namespace { + +// Frees the given va_list upon destruction. +// This also guards the returns from inside of the CHECK_NON_NULL_ARGUMENTs. +struct ScopedVAArgs { + explicit ScopedVAArgs(va_list* args): args(args) {} + ScopedVAArgs(const ScopedVAArgs&) = delete; + ScopedVAArgs(ScopedVAArgs&&) = delete; + ~ScopedVAArgs() { va_end(*args); } + + private: + va_list* args; +}; + +constexpr char kBadUtf8ReplacementChar[] = "?"; + +// This is a modified version of CountModifiedUtf8Chars() from utf.cc +// with extra checks and different output options. +// +// The `good` functor can process valid characters. +// The `bad` functor is called when we find an invalid character and +// returns true to abort processing, or false to continue processing. +// +// When aborted, VisitModifiedUtf8Chars() returns 0, otherwise the +// number of UTF-16 chars. +template +size_t VisitModifiedUtf8Chars(const char* utf8, size_t byte_count, GoodFunc good, BadFunc bad) { + DCHECK_LE(byte_count, strlen(utf8)); + size_t len = 0; + const char* end = utf8 + byte_count; + while (utf8 != end) { + int ic = *utf8; + if (LIKELY((ic & 0x80) == 0)) { + // One-byte encoding. + good(utf8, 1u); + utf8 += 1u; + len += 1u; + continue; + } + auto is_ascii = [utf8]() { + const char* ptr = utf8; // Make a copy that can be modified by GetUtf16FromUtf8(). + return mirror::String::IsASCII(dchecked_integral_cast(GetUtf16FromUtf8(&ptr))); + }; + // Note: Neither CountModifiedUtf8Chars() nor GetUtf16FromUtf8() checks whether + // the bit 0x40 is correctly set in the leading byte of a multi-byte sequence. + if ((ic & 0x20) == 0) { + // Two-byte encoding. + if (static_cast(end - utf8) < 2u) { + return bad() ? 0u : len + 1u; // Reached end of sequence + } + if (mirror::kUseStringCompression && is_ascii()) { + if (bad()) { + return 0u; + } + } else { + good(utf8, 2u); + } + utf8 += 2u; + len += 1u; + continue; + } + if ((ic & 0x10) == 0) { + // Three-byte encoding. + if (static_cast(end - utf8) < 3u) { + return bad() ? 0u : len + 1u; // Reached end of sequence + } + if (mirror::kUseStringCompression && is_ascii()) { + if (bad()) { + return 0u; + } + } else { + good(utf8, 3u); + } + utf8 += 3u; + len += 1u; + continue; + } + + // Four-byte encoding: needs to be converted into a surrogate pair. + // The decoded chars are never ASCII. + if (static_cast(end - utf8) < 4u) { + return bad() ? 0u : len + 1u; // Reached end of sequence + } + good(utf8, 4u); + utf8 += 4u; + len += 2u; + } + return len; +} + +} // namespace + +// Consider turning this on when there is errors which could be related to JNI array copies such as +// things not rendering correctly. E.g. b/16858794 +static constexpr bool kWarnJniAbort = false; + +static hiddenapi::AccessContext GetJniAccessContext(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Construct AccessContext from the first calling class on stack. + // If the calling class cannot be determined, e.g. unattached threads, + // we conservatively assume the caller is trusted. + ObjPtr caller = GetCallingClass(self, /* num_frames= */ 1); + return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true) + : hiddenapi::AccessContext(caller); +} + +template +ALWAYS_INLINE static bool ShouldDenyAccessToMember( + T* member, + Thread* self, + hiddenapi::AccessMethod access_kind = hiddenapi::AccessMethod::kJNI) + REQUIRES_SHARED(Locks::mutator_lock_) { + return hiddenapi::ShouldDenyAccessToMember( + member, + [self]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetJniAccessContext(self); }, + access_kind); +} + +// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set +// up handles for the rare case where these actually do something. Once these functions return it is +// possible there will be a pending exception if the instrumentation happens to throw one. +static void NotifySetObjectField(ArtField* field, jobject obj, jobject jval) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_EQ(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot); + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldWriteListeners())) { + Thread* self = Thread::Current(); + ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc=*/ nullptr, + /*check_suspended=*/ true, + /*abort_on_error=*/ false); + + if (cur_method == nullptr) { + // Set/Get Fields can be issued without a method during runtime startup/teardown. Ignore all + // of these changes. + return; + } + DCHECK(cur_method->IsNative()); + JValue val; + val.SetL(self->DecodeJObject(jval)); + instrumentation->FieldWriteEvent(self, + self->DecodeJObject(obj), + cur_method, + 0, // dex_pc is always 0 since this is a native method. + field, + val); + } +} + +static void NotifySetPrimitiveField(ArtField* field, jobject obj, JValue val) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_NE(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot); + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldWriteListeners())) { + Thread* self = Thread::Current(); + ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc=*/ nullptr, + /*check_suspended=*/ true, + /*abort_on_error=*/ false); + + if (cur_method == nullptr) { + // Set/Get Fields can be issued without a method during runtime startup/teardown. Ignore all + // of these changes. + return; + } + DCHECK(cur_method->IsNative()); + instrumentation->FieldWriteEvent(self, + self->DecodeJObject(obj), + cur_method, + 0, // dex_pc is always 0 since this is a native method. + field, + val); + } +} + +static void NotifyGetField(ArtField* field, jobject obj) + REQUIRES_SHARED(Locks::mutator_lock_) { + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldReadListeners())) { + Thread* self = Thread::Current(); + ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc=*/ nullptr, + /*check_suspended=*/ true, + /*abort_on_error=*/ false); + + if (cur_method == nullptr) { + // Set/Get Fields can be issued without a method during runtime startup/teardown. Ignore all + // of these changes. + return; + } + DCHECK(cur_method->IsNative()); + instrumentation->FieldReadEvent(self, + self->DecodeJObject(obj), + cur_method, + 0, // dex_pc is always 0 since this is a native method. + field); + } +} + +// Section 12.3.2 of the JNI spec describes JNI class descriptors. They're +// separated with slashes but aren't wrapped with "L;" like regular descriptors +// (i.e. "a/b/C" rather than "La/b/C;"). Arrays of reference types are an +// exception; there the "L;" must be present ("[La/b/C;"). Historically we've +// supported names with dots too (such as "a.b.C"). +static std::string NormalizeJniClassDescriptor(const char* name) { + std::string result; + // Add the missing "L;" if necessary. + if (name[0] == '[') { + result = name; + } else { + result += 'L'; + result += name; + result += ';'; + } + // Rewrite '.' as '/' for backwards compatibility. + if (result.find('.') != std::string::npos) { + LOG(WARNING) << "Call to JNI FindClass with dots in name: " + << "\"" << name << "\""; + std::replace(result.begin(), result.end(), '.', '/'); + } + return result; +} + +static void ReportInvalidJNINativeMethod(const ScopedObjectAccess& soa, + ObjPtr c, + const char* kind, + jint idx) + REQUIRES_SHARED(Locks::mutator_lock_) { + LOG(ERROR) + << "Failed to register native method in " << c->PrettyDescriptor() + << " in " << c->GetDexCache()->GetLocation()->ToModifiedUtf8() + << ": " << kind << " is null at index " << idx; + soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;", + "%s is null at index %d", + kind, + idx); +} + +template +static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class, + const char* name, const char* sig, bool is_static) + REQUIRES_SHARED(Locks::mutator_lock_) { + return jni::EncodeArtMethod(FindMethodJNI(soa, jni_class, name, sig, is_static)); +} + +template +static ObjPtr GetClassLoader(const ScopedObjectAccess& soa) + REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr); + // If we are running Runtime.nativeLoad, use the overriding ClassLoader it set. + if (method == + jni::DecodeArtMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) { + return soa.Decode(soa.Self()->GetClassLoaderOverride()); + } + // If we have a method, use its ClassLoader for context. + if (method != nullptr) { + return method->GetDeclaringClass()->GetClassLoader(); + } + // We don't have a method, so try to use the system ClassLoader. + ObjPtr class_loader = + soa.Decode(Runtime::Current()->GetSystemClassLoader()); + if (class_loader != nullptr) { + return class_loader; + } + // See if the override ClassLoader is set for gtests. + class_loader = soa.Decode(soa.Self()->GetClassLoaderOverride()); + if (class_loader != nullptr) { + // If so, CommonCompilerTest should have marked the runtime as a compiler not compiling an + // image. + CHECK(Runtime::Current()->IsAotCompiler()); + CHECK(!Runtime::Current()->IsCompilingBootImage()); + return class_loader; + } + // Use the BOOTCLASSPATH. + return nullptr; +} + +template +static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, const char* name, + const char* sig, bool is_static) + REQUIRES_SHARED(Locks::mutator_lock_) { + return jni::EncodeArtField(FindFieldJNI(soa, jni_class, name, sig, is_static)); +} + +static void ThrowAIOOBE(ScopedObjectAccess& soa, + ObjPtr array, + jsize start, + jsize length, + const char* identifier) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::string type(array->PrettyTypeOf()); + soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", + "%s offset=%d length=%d %s.length=%d", + type.c_str(), start, length, identifier, array->GetLength()); +} + +static void ThrowSIOOBE(ScopedObjectAccess& soa, jsize start, jsize length, + jsize array_length) + REQUIRES_SHARED(Locks::mutator_lock_) { + soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;", + "offset=%d length=%d string.length()=%d", start, length, + array_length); +} + +static void ThrowNoSuchMethodError(const ScopedObjectAccess& soa, + ObjPtr c, + const char* name, + const char* sig, + const char* kind) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::string temp; + soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;", + "no %s method \"%s.%s%s\"", + kind, + c->GetDescriptor(&temp), + name, + sig); +} + +static ObjPtr EnsureInitialized(Thread* self, ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (LIKELY(klass->IsInitialized())) { + return klass; + } + StackHandleScope<1> hs(self); + Handle h_klass(hs.NewHandle(klass)); + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_klass, true, true)) { + return nullptr; + } + return h_klass.Get(); +} + +ArtMethod* FindMethodJNI(const ScopedObjectAccess& soa, + jclass jni_class, + const char* name, + const char* sig, + bool is_static) { + ObjPtr c = EnsureInitialized(soa.Self(), soa.Decode(jni_class)); + if (c == nullptr) { + return nullptr; + } + ArtMethod* method = nullptr; + auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + if (c->IsInterface()) { + method = c->FindInterfaceMethod(name, sig, pointer_size); + } else { + method = c->FindClassMethod(name, sig, pointer_size); + } + if (method != nullptr && + ShouldDenyAccessToMember(method, soa.Self(), hiddenapi::AccessMethod::kNone)) { + // The resolved method that we have found cannot be accessed due to + // hiddenapi (typically it is declared up the hierarchy and is not an SDK + // method). Try to find an interface method from the implemented interfaces which is + // accessible. + ArtMethod* itf_method = c->FindAccessibleInterfaceMethod(method, pointer_size); + if (itf_method == nullptr) { + // No interface method. Call ShouldDenyAccessToMember again but this time + // with AccessMethod::kJNI to ensure that an appropriate warning is + // logged. + ShouldDenyAccessToMember(method, soa.Self(), hiddenapi::AccessMethod::kJNI); + method = nullptr; + } else { + // We found an interface method that is accessible, continue with the resolved method. + } + } + if (method == nullptr || method->IsStatic() != is_static) { + ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static"); + return nullptr; + } + return method; +} + +ArtField* FindFieldJNI(const ScopedObjectAccess& soa, + jclass jni_class, + const char* name, + const char* sig, + bool is_static) { + StackHandleScope<2> hs(soa.Self()); + Handle c( + hs.NewHandle(EnsureInitialized(soa.Self(), soa.Decode(jni_class)))); + if (c == nullptr) { + return nullptr; + } + ArtField* field = nullptr; + ObjPtr field_type; + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (UNLIKELY(sig[0] == '\0')) { + DCHECK(field == nullptr); + } else if (sig[1] != '\0') { + Handle class_loader(hs.NewHandle(c->GetClassLoader())); + field_type = class_linker->FindClass(soa.Self(), sig, class_loader); + } else { + field_type = class_linker->FindPrimitiveClass(*sig); + } + if (field_type == nullptr) { + // Failed to find type from the signature of the field. + DCHECK(sig[0] == '\0' || soa.Self()->IsExceptionPending()); + StackHandleScope<1> hs2(soa.Self()); + Handle cause(hs2.NewHandle(soa.Self()->GetException())); + soa.Self()->ClearException(); + std::string temp; + soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;", + "no type \"%s\" found and so no field \"%s\" " + "could be found in class \"%s\" or its superclasses", sig, name, + c->GetDescriptor(&temp)); + if (cause != nullptr) { + soa.Self()->GetException()->SetCause(cause.Get()); + } + return nullptr; + } + std::string temp; + if (is_static) { + field = mirror::Class::FindStaticField( + soa.Self(), c.Get(), name, field_type->GetDescriptor(&temp)); + } else { + field = c->FindInstanceField(name, field_type->GetDescriptor(&temp)); + } + if (field != nullptr && ShouldDenyAccessToMember(field, soa.Self())) { + field = nullptr; + } + if (field == nullptr) { + soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;", + "no \"%s\" field \"%s\" in class \"%s\" or its superclasses", + sig, name, c->GetDescriptor(&temp)); + return nullptr; + } + return field; +} + +int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause) + REQUIRES(!Locks::mutator_lock_) { + // Turn the const char* into a java.lang.String. + ScopedLocalRef s(env, env->NewStringUTF(msg)); + if (msg != nullptr && s.get() == nullptr) { + return JNI_ERR; + } + + // Choose an appropriate constructor and set up the arguments. + jvalue args[2]; + const char* signature; + if (msg == nullptr && cause == nullptr) { + signature = "()V"; + } else if (msg != nullptr && cause == nullptr) { + signature = "(Ljava/lang/String;)V"; + args[0].l = s.get(); + } else if (msg == nullptr && cause != nullptr) { + signature = "(Ljava/lang/Throwable;)V"; + args[0].l = cause; + } else { + signature = "(Ljava/lang/String;Ljava/lang/Throwable;)V"; + args[0].l = s.get(); + args[1].l = cause; + } + jmethodID mid = env->GetMethodID(exception_class, "", signature); + if (mid == nullptr) { + ScopedObjectAccess soa(env); + LOG(ERROR) << "No " << signature << " in " + << mirror::Class::PrettyClass(soa.Decode(exception_class)); + return JNI_ERR; + } + + ScopedLocalRef exception( + env, reinterpret_cast(env->NewObjectA(exception_class, mid, args))); + if (exception.get() == nullptr) { + return JNI_ERR; + } + ScopedObjectAccess soa(env); + soa.Self()->SetException(soa.Decode(exception.get())); + return JNI_OK; +} + +static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) { + return reinterpret_cast(env)->GetVm(); +} + +#define CHECK_NON_NULL_ARGUMENT(value) \ + CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, nullptr) + +#define CHECK_NON_NULL_ARGUMENT_RETURN_VOID(value) \ + CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, ) + +#define CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(value) \ + CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, 0) + +#define CHECK_NON_NULL_ARGUMENT_RETURN(value, return_val) \ + CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, return_val) + +#define CHECK_NON_NULL_ARGUMENT_FN_NAME(name, value, return_val) \ + if (UNLIKELY((value) == nullptr)) { \ + JavaVmExtFromEnv(env)->JniAbort(name, #value " == null"); \ + return return_val; \ + } + +#define CHECK_NON_NULL_MEMCPY_ARGUMENT(length, value) \ + if (UNLIKELY((length) != 0 && (value) == nullptr)) { \ + JavaVmExtFromEnv(env)->JniAbort(__FUNCTION__, #value " == null"); \ + return; \ + } + +template +static ArtMethod* FindMethod(ObjPtr c, + std::string_view name, + std::string_view sig) + REQUIRES_SHARED(Locks::mutator_lock_) { + auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + for (auto& method : c->GetMethods(pointer_size)) { + if (kNative == method.IsNative() && name == method.GetName() && method.GetSignature() == sig) { + return &method; + } + } + return nullptr; +} + +template +class JNI { + public: + static jint GetVersion(JNIEnv*) { + return JNI_VERSION_1_6; + } + + static jclass DefineClass(JNIEnv*, const char*, jobject, const jbyte*, jsize) { + LOG(WARNING) << "JNI DefineClass is not supported"; + return nullptr; + } + + static jclass FindClass(JNIEnv* env, const char* name) { + CHECK_NON_NULL_ARGUMENT(name); + + + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + std::string descriptor(NormalizeJniClassDescriptor(name)); + ScopedObjectAccess soa(env); + ObjPtr c = nullptr; + if (runtime->IsStarted()) { + StackHandleScope<1> hs(soa.Self()); + Handle class_loader(hs.NewHandle(GetClassLoader(soa))); + c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader); + } else { + c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str()); + } + return soa.AddLocalReference(c); + } + + static jmethodID FromReflectedMethod(JNIEnv* env, jobject jlr_method) { + CHECK_NON_NULL_ARGUMENT(jlr_method); + ScopedObjectAccess soa(env); + return jni::EncodeArtMethod(ArtMethod::FromReflectedMethod(soa, jlr_method)); + } + + static jfieldID FromReflectedField(JNIEnv* env, jobject jlr_field) { + CHECK_NON_NULL_ARGUMENT(jlr_field); + ScopedObjectAccess soa(env); + ObjPtr obj_field = soa.Decode(jlr_field); + if (obj_field->GetClass() != GetClassRoot()) { + // Not even a java.lang.reflect.Field, return null. TODO, is this check necessary? + return nullptr; + } + ObjPtr field = ObjPtr::DownCast(obj_field); + return jni::EncodeArtField(field->GetArtField()); + } + + static jobject ToReflectedMethod(JNIEnv* env, jclass, jmethodID mid, jboolean) { + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + ArtMethod* m = jni::DecodeArtMethod(mid); + ObjPtr method; + DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); + if (m->IsConstructor()) { + method = mirror::Constructor::CreateFromArtMethod(soa.Self(), m); + } else { + method = mirror::Method::CreateFromArtMethod(soa.Self(), m); + } + return soa.AddLocalReference(method); + } + + static jobject ToReflectedField(JNIEnv* env, jclass, jfieldID fid, jboolean) { + CHECK_NON_NULL_ARGUMENT(fid); + ScopedObjectAccess soa(env); + ArtField* f = jni::DecodeArtField(fid); + return soa.AddLocalReference( + mirror::Field::CreateFromArtField(soa.Self(), f, true)); + } + + static jclass GetObjectClass(JNIEnv* env, jobject java_object) { + CHECK_NON_NULL_ARGUMENT(java_object); + ScopedObjectAccess soa(env); + ObjPtr o = soa.Decode(java_object); + return soa.AddLocalReference(o->GetClass()); + } + + static jclass GetSuperclass(JNIEnv* env, jclass java_class) { + CHECK_NON_NULL_ARGUMENT(java_class); + ScopedObjectAccess soa(env); + ObjPtr c = soa.Decode(java_class); + return soa.AddLocalReference(c->IsInterface() ? nullptr : c->GetSuperClass()); + } + + // Note: java_class1 should be safely castable to java_class2, and + // not the other way around. + static jboolean IsAssignableFrom(JNIEnv* env, jclass java_class1, jclass java_class2) { + CHECK_NON_NULL_ARGUMENT_RETURN(java_class1, JNI_FALSE); + CHECK_NON_NULL_ARGUMENT_RETURN(java_class2, JNI_FALSE); + ScopedObjectAccess soa(env); + ObjPtr c1 = soa.Decode(java_class1); + ObjPtr c2 = soa.Decode(java_class2); + return c2->IsAssignableFrom(c1) ? JNI_TRUE : JNI_FALSE; + } + + static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass java_class) { + CHECK_NON_NULL_ARGUMENT_RETURN(java_class, JNI_FALSE); + if (jobj == nullptr) { + // Note: JNI is different from regular Java instanceof in this respect + return JNI_TRUE; + } else { + ScopedObjectAccess soa(env); + ObjPtr obj = soa.Decode(jobj); + ObjPtr c = soa.Decode(java_class); + return obj->InstanceOf(c) ? JNI_TRUE : JNI_FALSE; + } + } + + static jint Throw(JNIEnv* env, jthrowable java_exception) { + ScopedObjectAccess soa(env); + ObjPtr exception = soa.Decode(java_exception); + if (exception == nullptr) { + return JNI_ERR; + } + soa.Self()->SetException(exception); + return JNI_OK; + } + + static jint ThrowNew(JNIEnv* env, jclass c, const char* msg) { + CHECK_NON_NULL_ARGUMENT_RETURN(c, JNI_ERR); + return ThrowNewException(env, c, msg, nullptr); + } + + static jboolean ExceptionCheck(JNIEnv* env) { + return static_cast(env)->self_->IsExceptionPending() ? JNI_TRUE : JNI_FALSE; + } + + static void ExceptionClear(JNIEnv* env) { + ScopedObjectAccess soa(env); + soa.Self()->ClearException(); + } + + static void ExceptionDescribe(JNIEnv* env) { + ScopedObjectAccess soa(env); + + // If we have no exception to describe, pass through. + if (!soa.Self()->GetException()) { + return; + } + + StackHandleScope<1> hs(soa.Self()); + Handle old_exception( + hs.NewHandle(soa.Self()->GetException())); + soa.Self()->ClearException(); + ScopedLocalRef exception(env, + soa.AddLocalReference(old_exception.Get())); + ScopedLocalRef exception_class(env, env->GetObjectClass(exception.get())); + jmethodID mid = env->GetMethodID(exception_class.get(), "printStackTrace", "()V"); + if (mid == nullptr) { + LOG(WARNING) << "JNI WARNING: no printStackTrace()V in " + << mirror::Object::PrettyTypeOf(old_exception.Get()); + } else { + env->CallVoidMethod(exception.get(), mid); + if (soa.Self()->IsExceptionPending()) { + LOG(WARNING) << "JNI WARNING: " << mirror::Object::PrettyTypeOf(soa.Self()->GetException()) + << " thrown while calling printStackTrace"; + soa.Self()->ClearException(); + } + } + soa.Self()->SetException(old_exception.Get()); + } + + static jthrowable ExceptionOccurred(JNIEnv* env) { + ScopedObjectAccess soa(env); + ObjPtr exception = soa.Self()->GetException(); + return soa.AddLocalReference(exception); + } + + static void FatalError(JNIEnv*, const char* msg) { + LOG(FATAL) << "JNI FatalError called: " << msg; + } + + static jint PushLocalFrame(JNIEnv* env, jint capacity) { + // TODO: SOA may not be necessary but I do it to please lock annotations. + ScopedObjectAccess soa(env); + if (EnsureLocalCapacityInternal(soa, capacity, "PushLocalFrame") != JNI_OK) { + return JNI_ERR; + } + down_cast(env)->PushFrame(capacity); + return JNI_OK; + } + + static jobject PopLocalFrame(JNIEnv* env, jobject java_survivor) { + ScopedObjectAccess soa(env); + ObjPtr survivor = soa.Decode(java_survivor); + soa.Env()->PopFrame(); + return soa.AddLocalReference(survivor); + } + + static jint EnsureLocalCapacity(JNIEnv* env, jint desired_capacity) { + // TODO: SOA may not be necessary but I do it to please lock annotations. + ScopedObjectAccess soa(env); + return EnsureLocalCapacityInternal(soa, desired_capacity, "EnsureLocalCapacity"); + } + + static jobject NewGlobalRef(JNIEnv* env, jobject obj) { + ScopedObjectAccess soa(env); + ObjPtr decoded_obj = soa.Decode(obj); + return soa.Vm()->AddGlobalRef(soa.Self(), decoded_obj); + } + + static void DeleteGlobalRef(JNIEnv* env, jobject obj) { + JavaVMExt* vm = down_cast(env)->GetVm(); + Thread* self = down_cast(env)->self_; + vm->DeleteGlobalRef(self, obj); + } + + static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) { + ScopedObjectAccess soa(env); + ObjPtr decoded_obj = soa.Decode(obj); + return soa.Vm()->AddWeakGlobalRef(soa.Self(), decoded_obj); + } + + static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) { + JavaVMExt* vm = down_cast(env)->GetVm(); + Thread* self = down_cast(env)->self_; + vm->DeleteWeakGlobalRef(self, obj); + } + + static jobject NewLocalRef(JNIEnv* env, jobject obj) { + ScopedObjectAccess soa(env); + ObjPtr decoded_obj = soa.Decode(obj); + // Check for null after decoding the object to handle cleared weak globals. + if (decoded_obj == nullptr) { + return nullptr; + } + return soa.AddLocalReference(decoded_obj); + } + + static void DeleteLocalRef(JNIEnv* env, jobject obj) { + if (obj == nullptr) { + return; + } + // SOA is only necessary to have exclusion between GC root marking and removing. + // We don't want to have the GC attempt to mark a null root if we just removed + // it. b/22119403 + ScopedObjectAccess soa(env); + auto* ext_env = down_cast(env); + if (!ext_env->locals_.Remove(ext_env->local_ref_cookie_, obj)) { + // Attempting to delete a local reference that is not in the + // topmost local reference frame is a no-op. DeleteLocalRef returns + // void and doesn't throw any exceptions, but we should probably + // complain about it so the user will notice that things aren't + // going quite the way they expect. + LOG(WARNING) << "JNI WARNING: DeleteLocalRef(" << obj << ") " + << "failed to find entry"; + } + } + + static jboolean IsSameObject(JNIEnv* env, jobject obj1, jobject obj2) { + if (obj1 == obj2) { + return JNI_TRUE; + } else { + ScopedObjectAccess soa(env); + return (soa.Decode(obj1) == soa.Decode(obj2)) + ? JNI_TRUE : JNI_FALSE; + } + } + + static jobject AllocObject(JNIEnv* env, jclass java_class) { + CHECK_NON_NULL_ARGUMENT(java_class); + ScopedObjectAccess soa(env); + ObjPtr c = EnsureInitialized(soa.Self(), soa.Decode(java_class)); + if (c == nullptr) { + return nullptr; + } + if (c->IsStringClass()) { + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + return soa.AddLocalReference( + mirror::String::AllocEmptyString(soa.Self(), allocator_type)); + } + return soa.AddLocalReference(c->AllocObject(soa.Self())); + } + + static jobject NewObject(JNIEnv* env, jclass java_class, jmethodID mid, ...) { + va_list args; + va_start(args, mid); + ScopedVAArgs free_args_later(&args); + CHECK_NON_NULL_ARGUMENT(java_class); + CHECK_NON_NULL_ARGUMENT(mid); + + jobject result = NewObjectV(env, java_class, mid, args); + return result; + } + + static jobject NewObjectV(JNIEnv* env, jclass java_class, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT(java_class); + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + ObjPtr c = EnsureInitialized(soa.Self(), + soa.Decode(java_class)); + if (c == nullptr) { + return nullptr; + } + if (c->IsStringClass()) { + // Replace calls to String. with equivalent StringFactory call. + jmethodID sf_mid = jni::EncodeArtMethod( + WellKnownClasses::StringInitToStringFactory(jni::DecodeArtMethod(mid))); + return CallStaticObjectMethodV(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args); + } + ObjPtr result = c->AllocObject(soa.Self()); + if (result == nullptr) { + return nullptr; + } + jobject local_result = soa.AddLocalReference(result); + CallNonvirtualVoidMethodV(env, local_result, java_class, mid, args); + if (soa.Self()->IsExceptionPending()) { + return nullptr; + } + return local_result; + } + + static jobject NewObjectA(JNIEnv* env, jclass java_class, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT(java_class); + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + ObjPtr c = EnsureInitialized(soa.Self(), + soa.Decode(java_class)); + if (c == nullptr) { + return nullptr; + } + if (c->IsStringClass()) { + // Replace calls to String. with equivalent StringFactory call. + jmethodID sf_mid = jni::EncodeArtMethod( + WellKnownClasses::StringInitToStringFactory(jni::DecodeArtMethod(mid))); + return CallStaticObjectMethodA(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args); + } + ObjPtr result = c->AllocObject(soa.Self()); + if (result == nullptr) { + return nullptr; + } + jobject local_result = soa.AddLocalReference(result); + CallNonvirtualVoidMethodA(env, local_result, java_class, mid, args); + if (soa.Self()->IsExceptionPending()) { + return nullptr; + } + return local_result; + } + + static jmethodID GetMethodID(JNIEnv* env, jclass java_class, const char* name, const char* sig) { + CHECK_NON_NULL_ARGUMENT(java_class); + CHECK_NON_NULL_ARGUMENT(name); + CHECK_NON_NULL_ARGUMENT(sig); + ScopedObjectAccess soa(env); + jmethodID result = FindMethodID(soa, java_class, name, sig, false); + ShowVarArgs(soa,__FUNCTION__,java_class,name,sig,result); + return result; + } + + static jmethodID GetStaticMethodID(JNIEnv* env, jclass java_class, const char* name, + const char* sig) { + CHECK_NON_NULL_ARGUMENT(java_class); + CHECK_NON_NULL_ARGUMENT(name); + CHECK_NON_NULL_ARGUMENT(sig); + ScopedObjectAccess soa(env); + return FindMethodID(soa, java_class, name, sig, true); + } + + static jobject CallObjectMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT(obj); + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); + ShowVarArgs(soa,__FUNCTION__,mid,ap); + return soa.AddLocalReference(result.GetL()); + } + + static jobject CallObjectMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT(obj); + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args)); + jobject ret=soa.AddLocalReference(result.GetL()); + ShowVarArgs(soa,__FUNCTION__,mid,args,ret); + return ret; + } + + static jobject CallObjectMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT(obj); + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args)); + jobject ret=soa.AddLocalReference(result.GetL()); +// ShowVarArgs(soa,__FUNCTION__,mid,args,ret); + return ret; + } + + static jboolean CallBooleanMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); + return result.GetZ(); + } + + static jboolean CallBooleanMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetZ(); + } + + static jboolean CallBooleanMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetZ(); + } + + static jbyte CallByteMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); + return result.GetB(); + } + + static jbyte CallByteMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetB(); + } + + static jbyte CallByteMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetB(); + } + + static jchar CallCharMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); + return result.GetC(); + } + + static jchar CallCharMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetC(); + } + + static jchar CallCharMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetC(); + } + + static jdouble CallDoubleMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); + return result.GetD(); + } + + static jdouble CallDoubleMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetD(); + } + + static jdouble CallDoubleMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetD(); + } + + static jfloat CallFloatMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); + return result.GetF(); + } + + static jfloat CallFloatMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetF(); + } + + static jfloat CallFloatMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetF(); + } + + static jint CallIntMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); + return result.GetI(); + } + + static jint CallIntMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetI(); + } + + static jint CallIntMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetI(); + } + + static jlong CallLongMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); + return result.GetJ(); + } + + static jlong CallLongMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetJ(); + } + + static jlong CallLongMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetJ(); + } + + static jshort CallShortMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap)); + return result.GetS(); + } + + static jshort CallShortMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetS(); + } + + static jshort CallShortMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args).GetS(); + } + + static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); + ScopedObjectAccess soa(env); + InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap); + } + + static void CallVoidMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); + ScopedObjectAccess soa(env); + InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args); + } + + static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); + ScopedObjectAccess soa(env); + InvokeVirtualOrInterfaceWithJValues(soa, obj, mid, args); + } + + static jobject CallNonvirtualObjectMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT(obj); + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); + jobject local_result = soa.AddLocalReference(result.GetL()); + return local_result; + } + + static jobject CallNonvirtualObjectMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT(obj); + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, args)); + return soa.AddLocalReference(result.GetL()); + } + + static jobject CallNonvirtualObjectMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT(obj); + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithJValues(soa, obj, mid, args)); + return soa.AddLocalReference(result.GetL()); + } + + static jboolean CallNonvirtualBooleanMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, + ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); + return result.GetZ(); + } + + static jboolean CallNonvirtualBooleanMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, obj, mid, args).GetZ(); + } + + static jboolean CallNonvirtualBooleanMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, obj, mid, args).GetZ(); + } + + static jbyte CallNonvirtualByteMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); + return result.GetB(); + } + + static jbyte CallNonvirtualByteMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, obj, mid, args).GetB(); + } + + static jbyte CallNonvirtualByteMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, obj, mid, args).GetB(); + } + + static jchar CallNonvirtualCharMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); + return result.GetC(); + } + + static jchar CallNonvirtualCharMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, obj, mid, args).GetC(); + } + + static jchar CallNonvirtualCharMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, obj, mid, args).GetC(); + } + + static jshort CallNonvirtualShortMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); + return result.GetS(); + } + + static jshort CallNonvirtualShortMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, obj, mid, args).GetS(); + } + + static jshort CallNonvirtualShortMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, obj, mid, args).GetS(); + } + + static jint CallNonvirtualIntMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); + return result.GetI(); + } + + static jint CallNonvirtualIntMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, obj, mid, args).GetI(); + } + + static jint CallNonvirtualIntMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, obj, mid, args).GetI(); + } + + static jlong CallNonvirtualLongMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); + return result.GetJ(); + } + + static jlong CallNonvirtualLongMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, obj, mid, args).GetJ(); + } + + static jlong CallNonvirtualLongMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, obj, mid, args).GetJ(); + } + + static jfloat CallNonvirtualFloatMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); + return result.GetF(); + } + + static jfloat CallNonvirtualFloatMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, obj, mid, args).GetF(); + } + + static jfloat CallNonvirtualFloatMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, obj, mid, args).GetF(); + } + + static jdouble CallNonvirtualDoubleMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, obj, mid, ap)); + return result.GetD(); + } + + static jdouble CallNonvirtualDoubleMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, obj, mid, args).GetD(); + } + + static jdouble CallNonvirtualDoubleMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, obj, mid, args).GetD(); + } + + static void CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); + ScopedObjectAccess soa(env); + InvokeWithVarArgs(soa, obj, mid, ap); + } + + static void CallNonvirtualVoidMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid, + va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); + ScopedObjectAccess soa(env); + InvokeWithVarArgs(soa, obj, mid, args); + } + + static void CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid, + const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(obj); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); + ScopedObjectAccess soa(env); + InvokeWithJValues(soa, obj, mid, args); + } + + static jfieldID GetFieldID(JNIEnv* env, jclass java_class, const char* name, const char* sig) { + CHECK_NON_NULL_ARGUMENT(java_class); + CHECK_NON_NULL_ARGUMENT(name); + CHECK_NON_NULL_ARGUMENT(sig); + ScopedObjectAccess soa(env); + return FindFieldID(soa, java_class, name, sig, false); + } + + static jfieldID GetStaticFieldID(JNIEnv* env, jclass java_class, const char* name, + const char* sig) { + CHECK_NON_NULL_ARGUMENT(java_class); + CHECK_NON_NULL_ARGUMENT(name); + CHECK_NON_NULL_ARGUMENT(sig); + ScopedObjectAccess soa(env); + return FindFieldID(soa, java_class, name, sig, true); + } + + static jobject GetObjectField(JNIEnv* env, jobject obj, jfieldID fid) { + CHECK_NON_NULL_ARGUMENT(obj); + CHECK_NON_NULL_ARGUMENT(fid); + ScopedObjectAccess soa(env); + ArtField* f = jni::DecodeArtField(fid); + NotifyGetField(f, obj); + ObjPtr o = soa.Decode(obj); + return soa.AddLocalReference(f->GetObject(o)); + } + + static jobject GetStaticObjectField(JNIEnv* env, jclass, jfieldID fid) { + CHECK_NON_NULL_ARGUMENT(fid); + ScopedObjectAccess soa(env); + ArtField* f = jni::DecodeArtField(fid); + NotifyGetField(f, nullptr); + return soa.AddLocalReference(f->GetObject(f->GetDeclaringClass())); + } + + static void SetObjectField(JNIEnv* env, jobject java_object, jfieldID fid, jobject java_value) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_object); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); + ScopedObjectAccess soa(env); + ArtField* f = jni::DecodeArtField(fid); + NotifySetObjectField(f, java_object, java_value); + ObjPtr o = soa.Decode(java_object); + ObjPtr v = soa.Decode(java_value); + f->SetObject(o, v); + } + + static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); + ScopedObjectAccess soa(env); + ArtField* f = jni::DecodeArtField(fid); + NotifySetObjectField(f, nullptr, java_value); + ObjPtr v = soa.Decode(java_value); + f->SetObject(f->GetDeclaringClass(), v); + } + +#define GET_PRIMITIVE_FIELD(fn, instance) \ + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(instance); \ + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \ + ScopedObjectAccess soa(env); \ + ArtField* f = jni::DecodeArtField(fid); \ + NotifyGetField(f, instance); \ + ObjPtr o = soa.Decode(instance); \ + return f->Get ##fn (o) + +#define GET_STATIC_PRIMITIVE_FIELD(fn) \ + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \ + ScopedObjectAccess soa(env); \ + ArtField* f = jni::DecodeArtField(fid); \ + NotifyGetField(f, nullptr); \ + return f->Get ##fn (f->GetDeclaringClass()) + +#define SET_PRIMITIVE_FIELD(fn, instance, value) \ + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(instance); \ + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \ + ScopedObjectAccess soa(env); \ + ArtField* f = jni::DecodeArtField(fid); \ + NotifySetPrimitiveField(f, instance, JValue::FromPrimitive(value)); \ + ObjPtr o = soa.Decode(instance); \ + f->Set ##fn (o, value) + +#define SET_STATIC_PRIMITIVE_FIELD(fn, value) \ + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \ + ScopedObjectAccess soa(env); \ + ArtField* f = jni::DecodeArtField(fid); \ + NotifySetPrimitiveField(f, nullptr, JValue::FromPrimitive(value)); \ + f->Set ##fn (f->GetDeclaringClass(), value) + + static jboolean GetBooleanField(JNIEnv* env, jobject obj, jfieldID fid) { + GET_PRIMITIVE_FIELD(Boolean, obj); + } + + static jbyte GetByteField(JNIEnv* env, jobject obj, jfieldID fid) { + GET_PRIMITIVE_FIELD(Byte, obj); + } + + static jchar GetCharField(JNIEnv* env, jobject obj, jfieldID fid) { + GET_PRIMITIVE_FIELD(Char, obj); + } + + static jshort GetShortField(JNIEnv* env, jobject obj, jfieldID fid) { + GET_PRIMITIVE_FIELD(Short, obj); + } + + static jint GetIntField(JNIEnv* env, jobject obj, jfieldID fid) { + GET_PRIMITIVE_FIELD(Int, obj); + } + + static jlong GetLongField(JNIEnv* env, jobject obj, jfieldID fid) { + GET_PRIMITIVE_FIELD(Long, obj); + } + + static jfloat GetFloatField(JNIEnv* env, jobject obj, jfieldID fid) { + GET_PRIMITIVE_FIELD(Float, obj); + } + + static jdouble GetDoubleField(JNIEnv* env, jobject obj, jfieldID fid) { + GET_PRIMITIVE_FIELD(Double, obj); + } + + static jboolean GetStaticBooleanField(JNIEnv* env, jclass, jfieldID fid) { + GET_STATIC_PRIMITIVE_FIELD(Boolean); + } + + static jbyte GetStaticByteField(JNIEnv* env, jclass, jfieldID fid) { + GET_STATIC_PRIMITIVE_FIELD(Byte); + } + + static jchar GetStaticCharField(JNIEnv* env, jclass, jfieldID fid) { + GET_STATIC_PRIMITIVE_FIELD(Char); + } + + static jshort GetStaticShortField(JNIEnv* env, jclass, jfieldID fid) { + GET_STATIC_PRIMITIVE_FIELD(Short); + } + + static jint GetStaticIntField(JNIEnv* env, jclass, jfieldID fid) { + GET_STATIC_PRIMITIVE_FIELD(Int); + } + + static jlong GetStaticLongField(JNIEnv* env, jclass, jfieldID fid) { + GET_STATIC_PRIMITIVE_FIELD(Long); + } + + static jfloat GetStaticFloatField(JNIEnv* env, jclass, jfieldID fid) { + GET_STATIC_PRIMITIVE_FIELD(Float); + } + + static jdouble GetStaticDoubleField(JNIEnv* env, jclass, jfieldID fid) { + GET_STATIC_PRIMITIVE_FIELD(Double); + } + + static void SetBooleanField(JNIEnv* env, jobject obj, jfieldID fid, jboolean v) { + SET_PRIMITIVE_FIELD(Boolean, obj, v); + } + + static void SetByteField(JNIEnv* env, jobject obj, jfieldID fid, jbyte v) { + SET_PRIMITIVE_FIELD(Byte, obj, v); + } + + static void SetCharField(JNIEnv* env, jobject obj, jfieldID fid, jchar v) { + SET_PRIMITIVE_FIELD(Char, obj, v); + } + + static void SetFloatField(JNIEnv* env, jobject obj, jfieldID fid, jfloat v) { + SET_PRIMITIVE_FIELD(Float, obj, v); + } + + static void SetDoubleField(JNIEnv* env, jobject obj, jfieldID fid, jdouble v) { + SET_PRIMITIVE_FIELD(Double, obj, v); + } + + static void SetIntField(JNIEnv* env, jobject obj, jfieldID fid, jint v) { + SET_PRIMITIVE_FIELD(Int, obj, v); + } + + static void SetLongField(JNIEnv* env, jobject obj, jfieldID fid, jlong v) { + SET_PRIMITIVE_FIELD(Long, obj, v); + } + + static void SetShortField(JNIEnv* env, jobject obj, jfieldID fid, jshort v) { + SET_PRIMITIVE_FIELD(Short, obj, v); + } + + static void SetStaticBooleanField(JNIEnv* env, jclass, jfieldID fid, jboolean v) { + SET_STATIC_PRIMITIVE_FIELD(Boolean, v); + } + + static void SetStaticByteField(JNIEnv* env, jclass, jfieldID fid, jbyte v) { + SET_STATIC_PRIMITIVE_FIELD(Byte, v); + } + + static void SetStaticCharField(JNIEnv* env, jclass, jfieldID fid, jchar v) { + SET_STATIC_PRIMITIVE_FIELD(Char, v); + } + + static void SetStaticFloatField(JNIEnv* env, jclass, jfieldID fid, jfloat v) { + SET_STATIC_PRIMITIVE_FIELD(Float, v); + } + + static void SetStaticDoubleField(JNIEnv* env, jclass, jfieldID fid, jdouble v) { + SET_STATIC_PRIMITIVE_FIELD(Double, v); + } + + static void SetStaticIntField(JNIEnv* env, jclass, jfieldID fid, jint v) { + SET_STATIC_PRIMITIVE_FIELD(Int, v); + } + + static void SetStaticLongField(JNIEnv* env, jclass, jfieldID fid, jlong v) { + SET_STATIC_PRIMITIVE_FIELD(Long, v); + } + + static void SetStaticShortField(JNIEnv* env, jclass, jfieldID fid, jshort v) { + SET_STATIC_PRIMITIVE_FIELD(Short, v); + } + + static jobject CallStaticObjectMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); + jobject local_result = soa.AddLocalReference(result.GetL()); + return local_result; + } + + static jobject CallStaticObjectMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, args)); + return soa.AddLocalReference(result.GetL()); + } + + static jobject CallStaticObjectMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithJValues(soa, nullptr, mid, args)); + return soa.AddLocalReference(result.GetL()); + } + + static jboolean CallStaticBooleanMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); + return result.GetZ(); + } + + static jboolean CallStaticBooleanMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, nullptr, mid, args).GetZ(); + } + + static jboolean CallStaticBooleanMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, nullptr, mid, args).GetZ(); + } + + static jbyte CallStaticByteMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); + return result.GetB(); + } + + static jbyte CallStaticByteMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, nullptr, mid, args).GetB(); + } + + static jbyte CallStaticByteMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, nullptr, mid, args).GetB(); + } + + static jchar CallStaticCharMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); + return result.GetC(); + } + + static jchar CallStaticCharMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, nullptr, mid, args).GetC(); + } + + static jchar CallStaticCharMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, nullptr, mid, args).GetC(); + } + + static jshort CallStaticShortMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); + return result.GetS(); + } + + static jshort CallStaticShortMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, nullptr, mid, args).GetS(); + } + + static jshort CallStaticShortMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, nullptr, mid, args).GetS(); + } + + static jint CallStaticIntMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); + return result.GetI(); + } + + static jint CallStaticIntMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, nullptr, mid, args).GetI(); + } + + static jint CallStaticIntMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, nullptr, mid, args).GetI(); + } + + static jlong CallStaticLongMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); + return result.GetJ(); + } + + static jlong CallStaticLongMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, nullptr, mid, args).GetJ(); + } + + static jlong CallStaticLongMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, nullptr, mid, args).GetJ(); + } + + static jfloat CallStaticFloatMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); + return result.GetF(); + } + + static jfloat CallStaticFloatMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, nullptr, mid, args).GetF(); + } + + static jfloat CallStaticFloatMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, nullptr, mid, args).GetF(); + } + + static jdouble CallStaticDoubleMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap)); + return result.GetD(); + } + + static jdouble CallStaticDoubleMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithVarArgs(soa, nullptr, mid, args).GetD(); + } + + static jdouble CallStaticDoubleMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(mid); + ScopedObjectAccess soa(env); + return InvokeWithJValues(soa, nullptr, mid, args).GetD(); + } + + static void CallStaticVoidMethod(JNIEnv* env, jclass, jmethodID mid, ...) { + va_list ap; + va_start(ap, mid); + ScopedVAArgs free_args_later(&ap); + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); + ScopedObjectAccess soa(env); + InvokeWithVarArgs(soa, nullptr, mid, ap); + } + + static void CallStaticVoidMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); + ScopedObjectAccess soa(env); + InvokeWithVarArgs(soa, nullptr, mid, args); + } + + static void CallStaticVoidMethodA(JNIEnv* env, jclass, jmethodID mid, const jvalue* args) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid); + ScopedObjectAccess soa(env); + InvokeWithJValues(soa, nullptr, mid, args); + } + + static jstring NewString(JNIEnv* env, const jchar* chars, jsize char_count) { + if (UNLIKELY(char_count < 0)) { + JavaVmExtFromEnv(env)->JniAbortF("NewString", "char_count < 0: %d", char_count); + return nullptr; + } + if (UNLIKELY(chars == nullptr && char_count > 0)) { + JavaVmExtFromEnv(env)->JniAbortF("NewString", "chars == null && char_count > 0"); + return nullptr; + } + ScopedObjectAccess soa(env); + ObjPtr result = mirror::String::AllocFromUtf16(soa.Self(), char_count, chars); + return soa.AddLocalReference(result); + } + + static jstring NewStringUTF(JNIEnv* env, const char* utf) { + if (utf == nullptr) { + return nullptr; + } + + // The input may come from an untrusted source, so we need to validate it. + // We do not perform full validation, only as much as necessary to avoid reading + // beyond the terminating null character or breaking string compression invariants. + // CheckJNI performs stronger validation. + size_t utf8_length = strlen(utf); + if (UNLIKELY(utf8_length > static_cast(std::numeric_limits::max()))) { + // Converting the utf8_length to int32_t for String::AllocFromModifiedUtf8() would + // overflow. Throw OOME eagerly to avoid 2GiB allocation when trying to replace + // invalid sequences (even if such replacements could reduce the size below 2GiB). + std::string error = + android::base::StringPrintf("NewStringUTF input is 2 GiB or more: %zu", utf8_length); + ScopedObjectAccess soa(env); + soa.Self()->ThrowOutOfMemoryError(error.c_str()); + return nullptr; + } + + std::optional replacement_utf; + size_t utf16_length = VisitModifiedUtf8Chars( + utf, + utf8_length, + /*good=*/ [](const char* ptr ATTRIBUTE_UNUSED, size_t length ATTRIBUTE_UNUSED) {}, + /*bad=*/ []() { return true; }); // Abort processing and return 0 for bad characters. + if (UNLIKELY(utf8_length != 0u && utf16_length == 0u)) { + // VisitModifiedUtf8Chars() aborted for a bad character. + android_errorWriteLog(0x534e4554, "172655291"); // Report to SafetyNet. + // Report the error to logcat but avoid too much spam. + static const uint64_t kMinDelay = UINT64_C(10000000000); // 10s + static std::atomic prev_bad_input_time(UINT64_C(0)); + uint64_t prev_time = prev_bad_input_time.load(std::memory_order_relaxed); + uint64_t now = NanoTime(); + if ((prev_time == 0u || now - prev_time >= kMinDelay) && + prev_bad_input_time.compare_exchange_strong(prev_time, now, std::memory_order_relaxed)) { + LOG(ERROR) << "Invalid UTF-8 input to JNI::NewStringUTF()"; + } + // Copy the input to the `replacement_utf` and replace bad characters. + replacement_utf.emplace(); + replacement_utf->reserve(utf8_length); + utf16_length = VisitModifiedUtf8Chars( + utf, + utf8_length, + /*good=*/ [&](const char* ptr, size_t length) { + replacement_utf->append(ptr, length); + }, + /*bad=*/ [&]() { + replacement_utf->append(kBadUtf8ReplacementChar, sizeof(kBadUtf8ReplacementChar) - 1u); + return false; // Continue processing. + }); + utf = replacement_utf->c_str(); + utf8_length = replacement_utf->length(); + } + DCHECK_LE(utf16_length, utf8_length); + DCHECK_LE(utf8_length, static_cast(std::numeric_limits::max())); + + ScopedObjectAccess soa(env); + ShowVarArgs(soa,__FUNCTION__,utf); + ObjPtr result = + mirror::String::AllocFromModifiedUtf8(soa.Self(), utf16_length, utf, utf8_length); + return soa.AddLocalReference(result); + } + + static jsize GetStringLength(JNIEnv* env, jstring java_string) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(java_string); + ScopedObjectAccess soa(env); + return soa.Decode(java_string)->GetLength(); + } + + static jsize GetStringUTFLength(JNIEnv* env, jstring java_string) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(java_string); + ScopedObjectAccess soa(env); + return soa.Decode(java_string)->GetUtfLength(); + } + + static void GetStringRegion(JNIEnv* env, jstring java_string, jsize start, jsize length, + jchar* buf) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string); + ScopedObjectAccess soa(env); + ObjPtr s = soa.Decode(java_string); + if (start < 0 || length < 0 || length > s->GetLength() - start) { + ThrowSIOOBE(soa, start, length, s->GetLength()); + } else { + CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf); + if (s->IsCompressed()) { + const uint8_t* src = s->GetValueCompressed() + start; + for (int i = 0; i < length; ++i) { + buf[i] = static_cast(src[i]); + } + } else { + const jchar* chars = static_cast(s->GetValue()); + memcpy(buf, chars + start, length * sizeof(jchar)); + } + } + } + + static void GetStringUTFRegion(JNIEnv* env, jstring java_string, jsize start, jsize length, + char* buf) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string); + ScopedObjectAccess soa(env); + ObjPtr s = soa.Decode(java_string); + if (start < 0 || length < 0 || length > s->GetLength() - start) { + ThrowSIOOBE(soa, start, length, s->GetLength()); + } else { + CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf); + if (length == 0 && buf == nullptr) { + // Don't touch anything when length is 0 and null buffer. + return; + } + if (s->IsCompressed()) { + const uint8_t* src = s->GetValueCompressed() + start; + for (int i = 0; i < length; ++i) { + buf[i] = static_cast(src[i]); + } + buf[length] = '\0'; + } else { + const jchar* chars = s->GetValue(); + size_t bytes = CountUtf8Bytes(chars + start, length); + ConvertUtf16ToModifiedUtf8(buf, bytes, chars + start, length); + buf[bytes] = '\0'; + } + } + } + + static const jchar* GetStringChars(JNIEnv* env, jstring java_string, jboolean* is_copy) { + CHECK_NON_NULL_ARGUMENT(java_string); + ScopedObjectAccess soa(env); + ObjPtr s = soa.Decode(java_string); + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (heap->IsMovableObject(s) || s->IsCompressed()) { + jchar* chars = new jchar[s->GetLength()]; + if (s->IsCompressed()) { + int32_t length = s->GetLength(); + const uint8_t* src = s->GetValueCompressed(); + for (int i = 0; i < length; ++i) { + chars[i] = static_cast(src[i]); + } + } else { + memcpy(chars, s->GetValue(), sizeof(jchar) * s->GetLength()); + } + if (is_copy != nullptr) { + *is_copy = JNI_TRUE; + } + return chars; + } + if (is_copy != nullptr) { + *is_copy = JNI_FALSE; + } + return static_cast(s->GetValue()); + } + + static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar* chars) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string); + ScopedObjectAccess soa(env); + ObjPtr s = soa.Decode(java_string); + if (s->IsCompressed() || (s->IsCompressed() == false && chars != s->GetValue())) { + delete[] chars; + } + } + + static const jchar* GetStringCritical(JNIEnv* env, jstring java_string, jboolean* is_copy) { + CHECK_NON_NULL_ARGUMENT(java_string); + ScopedObjectAccess soa(env); + ObjPtr s = soa.Decode(java_string); + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (s->IsCompressed()) { + if (is_copy != nullptr) { + *is_copy = JNI_TRUE; + } + int32_t length = s->GetLength(); + const uint8_t* src = s->GetValueCompressed(); + jchar* chars = new jchar[length]; + for (int i = 0; i < length; ++i) { + chars[i] = static_cast(src[i]); + } + return chars; + } else { + if (heap->IsMovableObject(s)) { + StackHandleScope<1> hs(soa.Self()); + HandleWrapperObjPtr h(hs.NewHandleWrapper(&s)); + if (!kUseReadBarrier) { + heap->IncrementDisableMovingGC(soa.Self()); + } else { + // For the CC collector, we only need to wait for the thread flip rather + // than the whole GC to occur thanks to the to-space invariant. + heap->IncrementDisableThreadFlip(soa.Self()); + } + } + if (is_copy != nullptr) { + *is_copy = JNI_FALSE; + } + return static_cast(s->GetValue()); + } + } + + static void ReleaseStringCritical(JNIEnv* env, + jstring java_string, + const jchar* chars) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string); + ScopedObjectAccess soa(env); + gc::Heap* heap = Runtime::Current()->GetHeap(); + ObjPtr s = soa.Decode(java_string); + if (!s->IsCompressed() && heap->IsMovableObject(s)) { + if (!kUseReadBarrier) { + heap->DecrementDisableMovingGC(soa.Self()); + } else { + heap->DecrementDisableThreadFlip(soa.Self()); + } + } + // TODO: For uncompressed strings GetStringCritical() always returns `s->GetValue()`. + // Should we report an error if the user passes a different `chars`? + if (s->IsCompressed() || (!s->IsCompressed() && s->GetValue() != chars)) { + delete[] chars; + } + } + + static const char* GetStringUTFChars(JNIEnv* env, jstring java_string, jboolean* is_copy) { + if (java_string == nullptr) { + return nullptr; + } + if (is_copy != nullptr) { + *is_copy = JNI_TRUE; + } + + ScopedObjectAccess soa(env); + ObjPtr s = soa.Decode(java_string); + size_t byte_count = s->GetUtfLength(); + char* bytes = new char[byte_count + 1]; + CHECK(bytes != nullptr); // bionic aborts anyway. + if (s->IsCompressed()) { + const uint8_t* src = s->GetValueCompressed(); + for (size_t i = 0; i < byte_count; ++i) { + bytes[i] = src[i]; + } + } else { + const uint16_t* chars = s->GetValue(); + ConvertUtf16ToModifiedUtf8(bytes, byte_count, chars, s->GetLength()); + } + bytes[byte_count] = '\0'; + ShowVarArgs(soa,__FUNCTION__,is_copy,bytes); + return bytes; + } + + static void ReleaseStringUTFChars(JNIEnv*, jstring, const char* chars) { + delete[] chars; + } + + static jsize GetArrayLength(JNIEnv* env, jarray java_array) { + CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(java_array); + ScopedObjectAccess soa(env); + ObjPtr obj = soa.Decode(java_array); + if (UNLIKELY(!obj->IsArrayInstance())) { + soa.Vm()->JniAbortF("GetArrayLength", "not an array: %s", obj->PrettyTypeOf().c_str()); + return 0; + } + ObjPtr array = obj->AsArray(); + return array->GetLength(); + } + + static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray java_array, jsize index) { + CHECK_NON_NULL_ARGUMENT(java_array); + ScopedObjectAccess soa(env); + ObjPtr> array = + soa.Decode>(java_array); + return soa.AddLocalReference(array->Get(index)); + } + + static void SetObjectArrayElement(JNIEnv* env, jobjectArray java_array, jsize index, + jobject java_value) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); + ScopedObjectAccess soa(env); + ObjPtr> array = + soa.Decode>(java_array); + ObjPtr value = soa.Decode(java_value); + array->Set(index, value); + } + + static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) { + return NewPrimitiveArray(env, length); + } + + static jbyteArray NewByteArray(JNIEnv* env, jsize length) { + return NewPrimitiveArray(env, length); + } + + static jcharArray NewCharArray(JNIEnv* env, jsize length) { + return NewPrimitiveArray(env, length); + } + + static jdoubleArray NewDoubleArray(JNIEnv* env, jsize length) { + return NewPrimitiveArray(env, length); + } + + static jfloatArray NewFloatArray(JNIEnv* env, jsize length) { + return NewPrimitiveArray(env, length); + } + + static jintArray NewIntArray(JNIEnv* env, jsize length) { + return NewPrimitiveArray(env, length); + } + + static jlongArray NewLongArray(JNIEnv* env, jsize length) { + return NewPrimitiveArray(env, length); + } + + static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass, + jobject initial_element) { + if (UNLIKELY(length < 0)) { + JavaVmExtFromEnv(env)->JniAbortF("NewObjectArray", "negative array length: %d", length); + return nullptr; + } + CHECK_NON_NULL_ARGUMENT(element_jclass); + + // Compute the array class corresponding to the given element class. + ScopedObjectAccess soa(env); + ObjPtr array_class; + { + ObjPtr element_class = soa.Decode(element_jclass); + if (UNLIKELY(element_class->IsPrimitive())) { + soa.Vm()->JniAbortF("NewObjectArray", + "not an object type: %s", + element_class->PrettyDescriptor().c_str()); + return nullptr; + } + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + array_class = class_linker->FindArrayClass(soa.Self(), element_class); + if (UNLIKELY(array_class == nullptr)) { + return nullptr; + } + } + + // Allocate and initialize if necessary. + ObjPtr> result = + mirror::ObjectArray::Alloc(soa.Self(), array_class, length); + if (result != nullptr && initial_element != nullptr) { + ObjPtr initial_object = soa.Decode(initial_element); + if (initial_object != nullptr) { + ObjPtr element_class = result->GetClass()->GetComponentType(); + if (UNLIKELY(!element_class->IsAssignableFrom(initial_object->GetClass()))) { + soa.Vm()->JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with " + "element type of '%s'", + mirror::Class::PrettyDescriptor(initial_object->GetClass()).c_str(), + element_class->PrettyDescriptor().c_str()); + return nullptr; + } else { + for (jsize i = 0; i < length; ++i) { + result->SetWithoutChecks(i, initial_object); + } + } + } + } + return soa.AddLocalReference(result); + } + + static jshortArray NewShortArray(JNIEnv* env, jsize length) { + return NewPrimitiveArray(env, length); + } + + static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray java_array, jboolean* is_copy) { + CHECK_NON_NULL_ARGUMENT(java_array); + ScopedObjectAccess soa(env); + ObjPtr array = soa.Decode(java_array); + if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) { + soa.Vm()->JniAbortF("GetPrimitiveArrayCritical", "expected primitive array, given %s", + array->GetClass()->PrettyDescriptor().c_str()); + return nullptr; + } + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (heap->IsMovableObject(array)) { + if (!kUseReadBarrier) { + heap->IncrementDisableMovingGC(soa.Self()); + } else { + // For the CC collector, we only need to wait for the thread flip rather than the whole GC + // to occur thanks to the to-space invariant. + heap->IncrementDisableThreadFlip(soa.Self()); + } + // Re-decode in case the object moved since IncrementDisableGC waits for GC to complete. + array = soa.Decode(java_array); + } + if (is_copy != nullptr) { + *is_copy = JNI_FALSE; + } + return array->GetRawData(array->GetClass()->GetComponentSize(), 0); + } + + static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray java_array, void* elements, + jint mode) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); + ScopedObjectAccess soa(env); + ObjPtr array = soa.Decode(java_array); + if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) { + soa.Vm()->JniAbortF("ReleasePrimitiveArrayCritical", "expected primitive array, given %s", + array->GetClass()->PrettyDescriptor().c_str()); + return; + } + const size_t component_size = array->GetClass()->GetComponentSize(); + ReleasePrimitiveArray(soa, array, component_size, elements, mode); + } + + static jboolean* GetBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* is_copy) { + return GetPrimitiveArray(env, array, is_copy); + } + + static jbyte* GetByteArrayElements(JNIEnv* env, jbyteArray array, jboolean* is_copy) { + return GetPrimitiveArray(env, array, is_copy); + } + + static jchar* GetCharArrayElements(JNIEnv* env, jcharArray array, jboolean* is_copy) { + return GetPrimitiveArray(env, array, is_copy); + } + + static jdouble* GetDoubleArrayElements(JNIEnv* env, jdoubleArray array, jboolean* is_copy) { + return GetPrimitiveArray(env, array, is_copy); + } + + static jfloat* GetFloatArrayElements(JNIEnv* env, jfloatArray array, jboolean* is_copy) { + return GetPrimitiveArray(env, array, is_copy); + } + + static jint* GetIntArrayElements(JNIEnv* env, jintArray array, jboolean* is_copy) { + return GetPrimitiveArray(env, array, is_copy); + } + + static jlong* GetLongArrayElements(JNIEnv* env, jlongArray array, jboolean* is_copy) { + return GetPrimitiveArray(env, array, is_copy); + } + + static jshort* GetShortArrayElements(JNIEnv* env, jshortArray array, jboolean* is_copy) { + return GetPrimitiveArray(env, array, is_copy); + } + + static void ReleaseBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, + mode); + } + + static void ReleaseByteArrayElements(JNIEnv* env, jbyteArray array, jbyte* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); + } + + static void ReleaseCharArrayElements(JNIEnv* env, jcharArray array, jchar* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); + } + + static void ReleaseDoubleArrayElements(JNIEnv* env, jdoubleArray array, jdouble* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); + } + + static void ReleaseFloatArrayElements(JNIEnv* env, jfloatArray array, jfloat* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); + } + + static void ReleaseIntArrayElements(JNIEnv* env, jintArray array, jint* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); + } + + static void ReleaseLongArrayElements(JNIEnv* env, jlongArray array, jlong* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); + } + + static void ReleaseShortArrayElements(JNIEnv* env, jshortArray array, jshort* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); + } + + static void GetBooleanArrayRegion(JNIEnv* env, jbooleanArray array, jsize start, jsize length, + jboolean* buf) { + GetPrimitiveArrayRegion(env, array, start, + length, buf); + } + + static void GetByteArrayRegion(JNIEnv* env, jbyteArray array, jsize start, jsize length, + jbyte* buf) { + GetPrimitiveArrayRegion(env, array, start, length, buf); + } + + static void GetCharArrayRegion(JNIEnv* env, jcharArray array, jsize start, jsize length, + jchar* buf) { + GetPrimitiveArrayRegion(env, array, start, length, buf); + } + + static void GetDoubleArrayRegion(JNIEnv* env, jdoubleArray array, jsize start, jsize length, + jdouble* buf) { + GetPrimitiveArrayRegion(env, array, start, length, + buf); + } + + static void GetFloatArrayRegion(JNIEnv* env, jfloatArray array, jsize start, jsize length, + jfloat* buf) { + GetPrimitiveArrayRegion(env, array, start, length, + buf); + } + + static void GetIntArrayRegion(JNIEnv* env, jintArray array, jsize start, jsize length, + jint* buf) { + GetPrimitiveArrayRegion(env, array, start, length, buf); + } + + static void GetLongArrayRegion(JNIEnv* env, jlongArray array, jsize start, jsize length, + jlong* buf) { + GetPrimitiveArrayRegion(env, array, start, length, buf); + } + + static void GetShortArrayRegion(JNIEnv* env, jshortArray array, jsize start, jsize length, + jshort* buf) { + GetPrimitiveArrayRegion(env, array, start, length, + buf); + } + + static void SetBooleanArrayRegion(JNIEnv* env, jbooleanArray array, jsize start, jsize length, + const jboolean* buf) { + SetPrimitiveArrayRegion(env, array, start, + length, buf); + } + + static void SetByteArrayRegion(JNIEnv* env, jbyteArray array, jsize start, jsize length, + const jbyte* buf) { + SetPrimitiveArrayRegion(env, array, start, length, buf); + } + + static void SetCharArrayRegion(JNIEnv* env, jcharArray array, jsize start, jsize length, + const jchar* buf) { + SetPrimitiveArrayRegion(env, array, start, length, buf); + } + + static void SetDoubleArrayRegion(JNIEnv* env, jdoubleArray array, jsize start, jsize length, + const jdouble* buf) { + SetPrimitiveArrayRegion(env, array, start, length, + buf); + } + + static void SetFloatArrayRegion(JNIEnv* env, jfloatArray array, jsize start, jsize length, + const jfloat* buf) { + SetPrimitiveArrayRegion(env, array, start, length, + buf); + } + + static void SetIntArrayRegion(JNIEnv* env, jintArray array, jsize start, jsize length, + const jint* buf) { + SetPrimitiveArrayRegion(env, array, start, length, buf); + } + + static void SetLongArrayRegion(JNIEnv* env, jlongArray array, jsize start, jsize length, + const jlong* buf) { + SetPrimitiveArrayRegion(env, array, start, length, buf); + } + + static void SetShortArrayRegion(JNIEnv* env, jshortArray array, jsize start, jsize length, + const jshort* buf) { + SetPrimitiveArrayRegion(env, array, start, length, + buf); + } + + static jint RegisterNatives(JNIEnv* env, + jclass java_class, + const JNINativeMethod* methods, + jint method_count) { + if (UNLIKELY(method_count < 0)) { + JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d", + method_count); + return JNI_ERR; // Not reached except in unit tests. + } + CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + ScopedObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); + Handle c = hs.NewHandle(soa.Decode(java_class)); + if (UNLIKELY(method_count == 0)) { + LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for " + << c->PrettyDescriptor(); + return JNI_OK; + } + CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR); + for (jint i = 0; i < method_count; ++i) { + const char* name = methods[i].name; + const char* sig = methods[i].signature; + const void* fnPtr = methods[i].fnPtr; + if (UNLIKELY(name == nullptr)) { + ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i); + return JNI_ERR; + } else if (UNLIKELY(sig == nullptr)) { + ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i); + return JNI_ERR; + } else if (UNLIKELY(fnPtr == nullptr)) { + ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i); + return JNI_ERR; + } + bool is_fast = false; + // Notes about fast JNI calls: + // + // On a normal JNI call, the calling thread usually transitions + // from the kRunnable state to the kNative state. But if the + // called native function needs to access any Java object, it + // will have to transition back to the kRunnable state. + // + // There is a cost to this double transition. For a JNI call + // that should be quick, this cost may dominate the call cost. + // + // On a fast JNI call, the calling thread avoids this double + // transition by not transitioning from kRunnable to kNative and + // stays in the kRunnable state. + // + // There are risks to using a fast JNI call because it can delay + // a response to a thread suspension request which is typically + // used for a GC root scanning, etc. If a fast JNI call takes a + // long time, it could cause longer thread suspension latency + // and GC pauses. + // + // Thus, fast JNI should be used with care. It should be used + // for a JNI call that takes a short amount of time (eg. no + // long-running loop) and does not block (eg. no locks, I/O, + // etc.) + // + // A '!' prefix in the signature in the JNINativeMethod + // indicates that it's a fast JNI call and the runtime omits the + // thread state transition from kRunnable to kNative at the + // entry. + if (*sig == '!') { + is_fast = true; + ++sig; + } + + // Note: the right order is to try to find the method locally + // first, either as a direct or a virtual method. Then move to + // the parent. + ArtMethod* m = nullptr; + bool warn_on_going_to_parent = down_cast(env)->GetVm()->IsCheckJniEnabled(); + for (ObjPtr current_class = c.Get(); + current_class != nullptr; + current_class = current_class->GetSuperClass()) { + // Search first only comparing methods which are native. + m = FindMethod(current_class, name, sig); + if (m != nullptr) { + break; + } + + // Search again comparing to all methods, to find non-native methods that match. + m = FindMethod(current_class, name, sig); + if (m != nullptr) { + break; + } + + if (warn_on_going_to_parent) { + LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. " + << "This is slow, consider changing your RegisterNatives calls."; + warn_on_going_to_parent = false; + } + } + + if (m == nullptr) { + c->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail); + LOG(ERROR) + << "Failed to register native method " + << c->PrettyDescriptor() << "." << name << sig << " in " + << c->GetDexCache()->GetLocation()->ToModifiedUtf8(); + ThrowNoSuchMethodError(soa, c.Get(), name, sig, "static or non-static"); + return JNI_ERR; + } else if (!m->IsNative()) { + LOG(ERROR) + << "Failed to register non-native method " + << c->PrettyDescriptor() << "." << name << sig + << " as native"; + ThrowNoSuchMethodError(soa, c.Get(), name, sig, "native"); + return JNI_ERR; + } + + VLOG(jni) << "[Registering JNI native method " << m->PrettyMethod() << "]"; + + if (UNLIKELY(is_fast)) { + // There are a few reasons to switch: + // 1) We don't support !bang JNI anymore, it will turn to a hard error later. + // 2) @FastNative is actually faster. At least 1.5x faster than !bang JNI. + // and switching is super easy, remove ! in C code, add annotation in .java code. + // 3) Good chance of hitting DCHECK failures in ScopedFastNativeObjectAccess + // since that checks for presence of @FastNative and not for ! in the descriptor. + LOG(WARNING) << "!bang JNI is deprecated. Switch to @FastNative for " << m->PrettyMethod(); + is_fast = false; + // TODO: make this a hard register error in the future. + } + + const void* final_function_ptr = class_linker->RegisterNative(soa.Self(), m, fnPtr); + UNUSED(final_function_ptr); + } + return JNI_OK; + } + + static jint UnregisterNatives(JNIEnv* env, jclass java_class) { + CHECK_NON_NULL_ARGUMENT_RETURN(java_class, JNI_ERR); + ScopedObjectAccess soa(env); + ObjPtr c = soa.Decode(java_class); + + VLOG(jni) << "[Unregistering JNI native methods for " << mirror::Class::PrettyClass(c) << "]"; + + size_t unregistered_count = 0; + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + auto pointer_size = class_linker->GetImagePointerSize(); + for (auto& m : c->GetMethods(pointer_size)) { + if (m.IsNative()) { + class_linker->UnregisterNative(soa.Self(), &m); + unregistered_count++; + } + } + + if (unregistered_count == 0) { + LOG(WARNING) << "JNI UnregisterNatives: attempt to unregister native methods of class '" + << mirror::Class::PrettyDescriptor(c) << "' that contains no native methods"; + } + return JNI_OK; + } + + static jint MonitorEnter(JNIEnv* env, jobject java_object) NO_THREAD_SAFETY_ANALYSIS { + CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNI_ERR); + ScopedObjectAccess soa(env); + ObjPtr o = soa.Decode(java_object); + o = o->MonitorEnter(soa.Self()); + if (soa.Self()->HoldsLock(o)) { + soa.Env()->monitors_.Add(o); + } + if (soa.Self()->IsExceptionPending()) { + return JNI_ERR; + } + return JNI_OK; + } + + static jint MonitorExit(JNIEnv* env, jobject java_object) NO_THREAD_SAFETY_ANALYSIS { + CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNI_ERR); + ScopedObjectAccess soa(env); + ObjPtr o = soa.Decode(java_object); + bool remove_mon = soa.Self()->HoldsLock(o); + o->MonitorExit(soa.Self()); + if (remove_mon) { + soa.Env()->monitors_.Remove(o); + } + if (soa.Self()->IsExceptionPending()) { + return JNI_ERR; + } + return JNI_OK; + } + + static jint GetJavaVM(JNIEnv* env, JavaVM** vm) { + CHECK_NON_NULL_ARGUMENT_RETURN(vm, JNI_ERR); + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr) { + *vm = runtime->GetJavaVM(); + } else { + *vm = nullptr; + } + return (*vm != nullptr) ? JNI_OK : JNI_ERR; + } + + static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) { + if (capacity < 0) { + JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %" PRId64, + capacity); + return nullptr; + } + if (address == nullptr && capacity != 0) { + JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer", + "non-zero capacity for nullptr pointer: %" PRId64, capacity); + return nullptr; + } + + // At the moment, the capacity of DirectByteBuffer is limited to a signed int. + if (capacity > INT_MAX) { + JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer", + "buffer capacity greater than maximum jint: %" PRId64, + capacity); + return nullptr; + } + jlong address_arg = reinterpret_cast(address); + jint capacity_arg = static_cast(capacity); + + jobject result = env->NewObject(WellKnownClasses::java_nio_DirectByteBuffer, + WellKnownClasses::java_nio_DirectByteBuffer_init, + address_arg, capacity_arg); + return static_cast(env)->self_->IsExceptionPending() ? nullptr : result; + } + + static void* GetDirectBufferAddress(JNIEnv* env, jobject java_buffer) { + // Return null if |java_buffer| is not defined. + if (java_buffer == nullptr) { + return nullptr; + } + + // Return null if |java_buffer| is not a java.nio.Buffer instance. + if (!IsInstanceOf(env, java_buffer, WellKnownClasses::java_nio_Buffer)) { + return nullptr; + } + + // Buffer.address is non-null when the |java_buffer| is direct. + return reinterpret_cast(env->GetLongField( + java_buffer, WellKnownClasses::java_nio_Buffer_address)); + } + + static jlong GetDirectBufferCapacity(JNIEnv* env, jobject java_buffer) { + if (java_buffer == nullptr) { + return -1; + } + + if (!IsInstanceOf(env, java_buffer, WellKnownClasses::java_nio_Buffer)) { + return -1; + } + + // When checking the buffer capacity, it's important to note that a zero-sized direct buffer + // may have a null address field which means we can't tell whether it is direct or not. + // We therefore call Buffer.isDirect(). One path that creates such a buffer is + // FileChannel.map() if the file size is zero. + // + // NB GetDirectBufferAddress() does not need to call Buffer.isDirect() since it is only + // able return a valid address if the Buffer address field is not-null. + jboolean direct = env->CallBooleanMethod(java_buffer, + WellKnownClasses::java_nio_Buffer_isDirect); + if (!direct) { + return -1; + } + + return static_cast(env->GetIntField( + java_buffer, WellKnownClasses::java_nio_Buffer_capacity)); + } + + static jobjectRefType GetObjectRefType(JNIEnv* env ATTRIBUTE_UNUSED, jobject java_object) { + if (java_object == nullptr) { + return JNIInvalidRefType; + } + + // Do we definitely know what kind of reference this is? + IndirectRef ref = reinterpret_cast(java_object); + IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref); + switch (kind) { + case kLocal: + return JNILocalRefType; + case kGlobal: + return JNIGlobalRefType; + case kWeakGlobal: + return JNIWeakGlobalRefType; + case kJniTransitionOrInvalid: + // Assume value is in a JNI transition frame. + return JNILocalRefType; + } + LOG(FATAL) << "IndirectRefKind[" << kind << "]"; + UNREACHABLE(); + } + + private: + static jint EnsureLocalCapacityInternal(ScopedObjectAccess& soa, jint desired_capacity, + const char* caller) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (desired_capacity < 0) { + LOG(ERROR) << "Invalid capacity given to " << caller << ": " << desired_capacity; + return JNI_ERR; + } + + std::string error_msg; + if (!soa.Env()->locals_.EnsureFreeCapacity(static_cast(desired_capacity), &error_msg)) { + std::string caller_error = android::base::StringPrintf("%s: %s", caller, error_msg.c_str()); + soa.Self()->ThrowOutOfMemoryError(caller_error.c_str()); + return JNI_ERR; + } + return JNI_OK; + } + + template + static JniT NewPrimitiveArray(JNIEnv* env, jsize length) { + ScopedObjectAccess soa(env); + if (UNLIKELY(length < 0)) { + soa.Vm()->JniAbortF("NewPrimitiveArray", "negative array length: %d", length); + return nullptr; + } + ObjPtr result = ArtT::Alloc(soa.Self(), length); + return soa.AddLocalReference(result); + } + + template + static ObjPtr DecodeAndCheckArrayType(ScopedObjectAccess& soa, + JArrayT java_array, + const char* fn_name, + const char* operation) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr array = soa.Decode(java_array); + ObjPtr expected_array_class = GetClassRoot(); + if (UNLIKELY(expected_array_class != array->GetClass())) { + soa.Vm()->JniAbortF(fn_name, + "attempt to %s %s primitive array elements with an object of type %s", + operation, + mirror::Class::PrettyDescriptor( + expected_array_class->GetComponentType()).c_str(), + mirror::Class::PrettyDescriptor(array->GetClass()).c_str()); + return nullptr; + } + DCHECK_EQ(sizeof(ElementT), array->GetClass()->GetComponentSize()); + return array; + } + + template + static ElementT* GetPrimitiveArray(JNIEnv* env, ArrayT java_array, jboolean* is_copy) { + CHECK_NON_NULL_ARGUMENT(java_array); + ScopedObjectAccess soa(env); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "GetArrayElements", "get"); + if (UNLIKELY(array == nullptr)) { + return nullptr; + } + // Only make a copy if necessary. + if (Runtime::Current()->GetHeap()->IsMovableObject(array)) { + if (is_copy != nullptr) { + *is_copy = JNI_TRUE; + } + const size_t component_size = sizeof(ElementT); + size_t size = array->GetLength() * component_size; + void* data = new uint64_t[RoundUp(size, 8) / 8]; + memcpy(data, array->GetData(), size); + return reinterpret_cast(data); + } else { + if (is_copy != nullptr) { + *is_copy = JNI_FALSE; + } + return reinterpret_cast(array->GetData()); + } + } + + template + static void ReleasePrimitiveArray(JNIEnv* env, ArrayT java_array, ElementT* elements, jint mode) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); + ScopedObjectAccess soa(env); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "ReleaseArrayElements", "release"); + if (array == nullptr) { + return; + } + ReleasePrimitiveArray(soa, array, sizeof(ElementT), elements, mode); + } + + static void ReleasePrimitiveArray(ScopedObjectAccess& soa, + ObjPtr array, + size_t component_size, + void* elements, + jint mode) + REQUIRES_SHARED(Locks::mutator_lock_) { + void* array_data = array->GetRawData(component_size, 0); + gc::Heap* heap = Runtime::Current()->GetHeap(); + bool is_copy = array_data != elements; + size_t bytes = array->GetLength() * component_size; + if (is_copy) { + // Integrity check: If elements is not the same as the java array's data, it better not be a + // heap address. TODO: This might be slow to check, may be worth keeping track of which + // copies we make? + if (heap->IsNonDiscontinuousSpaceHeapAddress(elements)) { + soa.Vm()->JniAbortF("ReleaseArrayElements", + "invalid element pointer %p, array elements are %p", + reinterpret_cast(elements), array_data); + return; + } + if (mode != JNI_ABORT) { + memcpy(array_data, elements, bytes); + } else if (kWarnJniAbort && memcmp(array_data, elements, bytes) != 0) { + // Warn if we have JNI_ABORT and the arrays don't match since this is usually an error. + LOG(WARNING) << "Possible incorrect JNI_ABORT in Release*ArrayElements"; + soa.Self()->DumpJavaStack(LOG_STREAM(WARNING)); + } + } + if (mode != JNI_COMMIT) { + if (is_copy) { + delete[] reinterpret_cast(elements); + } else if (heap->IsMovableObject(array)) { + // Non copy to a movable object must means that we had disabled the moving GC. + if (!kUseReadBarrier) { + heap->DecrementDisableMovingGC(soa.Self()); + } else { + heap->DecrementDisableThreadFlip(soa.Self()); + } + } + } + } + + template + static void GetPrimitiveArrayRegion(JNIEnv* env, JArrayT java_array, + jsize start, jsize length, ElementT* buf) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); + ScopedObjectAccess soa(env); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "GetPrimitiveArrayRegion", "get region of"); + if (array != nullptr) { + if (start < 0 || length < 0 || length > array->GetLength() - start) { + ThrowAIOOBE(soa, array, start, length, "src"); + } else { + CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf); + ElementT* data = array->GetData(); + memcpy(buf, data + start, length * sizeof(ElementT)); + } + } + } + + template + static void SetPrimitiveArrayRegion(JNIEnv* env, JArrayT java_array, + jsize start, jsize length, const ElementT* buf) { + CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_array); + ScopedObjectAccess soa(env); + ObjPtr array = DecodeAndCheckArrayType( + soa, java_array, "SetPrimitiveArrayRegion", "set region of"); + if (array != nullptr) { + if (start < 0 || length < 0 || length > array->GetLength() - start) { + ThrowAIOOBE(soa, array, start, length, "dst"); + } else { + CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf); + ElementT* data = array->GetData(); + memcpy(data + start, buf, length * sizeof(ElementT)); + } + } + } +}; + +template +struct JniNativeInterfaceFunctions { + using JNIImpl = JNI; + static constexpr JNINativeInterface gJniNativeInterface = { + nullptr, // reserved0. + nullptr, // reserved1. + nullptr, // reserved2. + nullptr, // reserved3. + JNIImpl::GetVersion, + JNIImpl::DefineClass, + JNIImpl::FindClass, + JNIImpl::FromReflectedMethod, + JNIImpl::FromReflectedField, + JNIImpl::ToReflectedMethod, + JNIImpl::GetSuperclass, + JNIImpl::IsAssignableFrom, + JNIImpl::ToReflectedField, + JNIImpl::Throw, + JNIImpl::ThrowNew, + JNIImpl::ExceptionOccurred, + JNIImpl::ExceptionDescribe, + JNIImpl::ExceptionClear, + JNIImpl::FatalError, + JNIImpl::PushLocalFrame, + JNIImpl::PopLocalFrame, + JNIImpl::NewGlobalRef, + JNIImpl::DeleteGlobalRef, + JNIImpl::DeleteLocalRef, + JNIImpl::IsSameObject, + JNIImpl::NewLocalRef, + JNIImpl::EnsureLocalCapacity, + JNIImpl::AllocObject, + JNIImpl::NewObject, + JNIImpl::NewObjectV, + JNIImpl::NewObjectA, + JNIImpl::GetObjectClass, + JNIImpl::IsInstanceOf, + JNIImpl::GetMethodID, + JNIImpl::CallObjectMethod, + JNIImpl::CallObjectMethodV, + JNIImpl::CallObjectMethodA, + JNIImpl::CallBooleanMethod, + JNIImpl::CallBooleanMethodV, + JNIImpl::CallBooleanMethodA, + JNIImpl::CallByteMethod, + JNIImpl::CallByteMethodV, + JNIImpl::CallByteMethodA, + JNIImpl::CallCharMethod, + JNIImpl::CallCharMethodV, + JNIImpl::CallCharMethodA, + JNIImpl::CallShortMethod, + JNIImpl::CallShortMethodV, + JNIImpl::CallShortMethodA, + JNIImpl::CallIntMethod, + JNIImpl::CallIntMethodV, + JNIImpl::CallIntMethodA, + JNIImpl::CallLongMethod, + JNIImpl::CallLongMethodV, + JNIImpl::CallLongMethodA, + JNIImpl::CallFloatMethod, + JNIImpl::CallFloatMethodV, + JNIImpl::CallFloatMethodA, + JNIImpl::CallDoubleMethod, + JNIImpl::CallDoubleMethodV, + JNIImpl::CallDoubleMethodA, + JNIImpl::CallVoidMethod, + JNIImpl::CallVoidMethodV, + JNIImpl::CallVoidMethodA, + JNIImpl::CallNonvirtualObjectMethod, + JNIImpl::CallNonvirtualObjectMethodV, + JNIImpl::CallNonvirtualObjectMethodA, + JNIImpl::CallNonvirtualBooleanMethod, + JNIImpl::CallNonvirtualBooleanMethodV, + JNIImpl::CallNonvirtualBooleanMethodA, + JNIImpl::CallNonvirtualByteMethod, + JNIImpl::CallNonvirtualByteMethodV, + JNIImpl::CallNonvirtualByteMethodA, + JNIImpl::CallNonvirtualCharMethod, + JNIImpl::CallNonvirtualCharMethodV, + JNIImpl::CallNonvirtualCharMethodA, + JNIImpl::CallNonvirtualShortMethod, + JNIImpl::CallNonvirtualShortMethodV, + JNIImpl::CallNonvirtualShortMethodA, + JNIImpl::CallNonvirtualIntMethod, + JNIImpl::CallNonvirtualIntMethodV, + JNIImpl::CallNonvirtualIntMethodA, + JNIImpl::CallNonvirtualLongMethod, + JNIImpl::CallNonvirtualLongMethodV, + JNIImpl::CallNonvirtualLongMethodA, + JNIImpl::CallNonvirtualFloatMethod, + JNIImpl::CallNonvirtualFloatMethodV, + JNIImpl::CallNonvirtualFloatMethodA, + JNIImpl::CallNonvirtualDoubleMethod, + JNIImpl::CallNonvirtualDoubleMethodV, + JNIImpl::CallNonvirtualDoubleMethodA, + JNIImpl::CallNonvirtualVoidMethod, + JNIImpl::CallNonvirtualVoidMethodV, + JNIImpl::CallNonvirtualVoidMethodA, + JNIImpl::GetFieldID, + JNIImpl::GetObjectField, + JNIImpl::GetBooleanField, + JNIImpl::GetByteField, + JNIImpl::GetCharField, + JNIImpl::GetShortField, + JNIImpl::GetIntField, + JNIImpl::GetLongField, + JNIImpl::GetFloatField, + JNIImpl::GetDoubleField, + JNIImpl::SetObjectField, + JNIImpl::SetBooleanField, + JNIImpl::SetByteField, + JNIImpl::SetCharField, + JNIImpl::SetShortField, + JNIImpl::SetIntField, + JNIImpl::SetLongField, + JNIImpl::SetFloatField, + JNIImpl::SetDoubleField, + JNIImpl::GetStaticMethodID, + JNIImpl::CallStaticObjectMethod, + JNIImpl::CallStaticObjectMethodV, + JNIImpl::CallStaticObjectMethodA, + JNIImpl::CallStaticBooleanMethod, + JNIImpl::CallStaticBooleanMethodV, + JNIImpl::CallStaticBooleanMethodA, + JNIImpl::CallStaticByteMethod, + JNIImpl::CallStaticByteMethodV, + JNIImpl::CallStaticByteMethodA, + JNIImpl::CallStaticCharMethod, + JNIImpl::CallStaticCharMethodV, + JNIImpl::CallStaticCharMethodA, + JNIImpl::CallStaticShortMethod, + JNIImpl::CallStaticShortMethodV, + JNIImpl::CallStaticShortMethodA, + JNIImpl::CallStaticIntMethod, + JNIImpl::CallStaticIntMethodV, + JNIImpl::CallStaticIntMethodA, + JNIImpl::CallStaticLongMethod, + JNIImpl::CallStaticLongMethodV, + JNIImpl::CallStaticLongMethodA, + JNIImpl::CallStaticFloatMethod, + JNIImpl::CallStaticFloatMethodV, + JNIImpl::CallStaticFloatMethodA, + JNIImpl::CallStaticDoubleMethod, + JNIImpl::CallStaticDoubleMethodV, + JNIImpl::CallStaticDoubleMethodA, + JNIImpl::CallStaticVoidMethod, + JNIImpl::CallStaticVoidMethodV, + JNIImpl::CallStaticVoidMethodA, + JNIImpl::GetStaticFieldID, + JNIImpl::GetStaticObjectField, + JNIImpl::GetStaticBooleanField, + JNIImpl::GetStaticByteField, + JNIImpl::GetStaticCharField, + JNIImpl::GetStaticShortField, + JNIImpl::GetStaticIntField, + JNIImpl::GetStaticLongField, + JNIImpl::GetStaticFloatField, + JNIImpl::GetStaticDoubleField, + JNIImpl::SetStaticObjectField, + JNIImpl::SetStaticBooleanField, + JNIImpl::SetStaticByteField, + JNIImpl::SetStaticCharField, + JNIImpl::SetStaticShortField, + JNIImpl::SetStaticIntField, + JNIImpl::SetStaticLongField, + JNIImpl::SetStaticFloatField, + JNIImpl::SetStaticDoubleField, + JNIImpl::NewString, + JNIImpl::GetStringLength, + JNIImpl::GetStringChars, + JNIImpl::ReleaseStringChars, + JNIImpl::NewStringUTF, + JNIImpl::GetStringUTFLength, + JNIImpl::GetStringUTFChars, + JNIImpl::ReleaseStringUTFChars, + JNIImpl::GetArrayLength, + JNIImpl::NewObjectArray, + JNIImpl::GetObjectArrayElement, + JNIImpl::SetObjectArrayElement, + JNIImpl::NewBooleanArray, + JNIImpl::NewByteArray, + JNIImpl::NewCharArray, + JNIImpl::NewShortArray, + JNIImpl::NewIntArray, + JNIImpl::NewLongArray, + JNIImpl::NewFloatArray, + JNIImpl::NewDoubleArray, + JNIImpl::GetBooleanArrayElements, + JNIImpl::GetByteArrayElements, + JNIImpl::GetCharArrayElements, + JNIImpl::GetShortArrayElements, + JNIImpl::GetIntArrayElements, + JNIImpl::GetLongArrayElements, + JNIImpl::GetFloatArrayElements, + JNIImpl::GetDoubleArrayElements, + JNIImpl::ReleaseBooleanArrayElements, + JNIImpl::ReleaseByteArrayElements, + JNIImpl::ReleaseCharArrayElements, + JNIImpl::ReleaseShortArrayElements, + JNIImpl::ReleaseIntArrayElements, + JNIImpl::ReleaseLongArrayElements, + JNIImpl::ReleaseFloatArrayElements, + JNIImpl::ReleaseDoubleArrayElements, + JNIImpl::GetBooleanArrayRegion, + JNIImpl::GetByteArrayRegion, + JNIImpl::GetCharArrayRegion, + JNIImpl::GetShortArrayRegion, + JNIImpl::GetIntArrayRegion, + JNIImpl::GetLongArrayRegion, + JNIImpl::GetFloatArrayRegion, + JNIImpl::GetDoubleArrayRegion, + JNIImpl::SetBooleanArrayRegion, + JNIImpl::SetByteArrayRegion, + JNIImpl::SetCharArrayRegion, + JNIImpl::SetShortArrayRegion, + JNIImpl::SetIntArrayRegion, + JNIImpl::SetLongArrayRegion, + JNIImpl::SetFloatArrayRegion, + JNIImpl::SetDoubleArrayRegion, + JNIImpl::RegisterNatives, + JNIImpl::UnregisterNatives, + JNIImpl::MonitorEnter, + JNIImpl::MonitorExit, + JNIImpl::GetJavaVM, + JNIImpl::GetStringRegion, + JNIImpl::GetStringUTFRegion, + JNIImpl::GetPrimitiveArrayCritical, + JNIImpl::ReleasePrimitiveArrayCritical, + JNIImpl::GetStringCritical, + JNIImpl::ReleaseStringCritical, + JNIImpl::NewWeakGlobalRef, + JNIImpl::DeleteWeakGlobalRef, + JNIImpl::ExceptionCheck, + JNIImpl::NewDirectByteBuffer, + JNIImpl::GetDirectBufferAddress, + JNIImpl::GetDirectBufferCapacity, + JNIImpl::GetObjectRefType, + }; +}; + +const JNINativeInterface* GetJniNativeInterface() { + // The template argument is passed down through the Encode/DecodeArtMethod/Field calls so if + // JniIdType is kPointer the calls will be a simple cast with no branches. This ensures that + // the normal case is still fast. + return Runtime::Current()->GetJniIdType() == JniIdType::kPointer + ? &JniNativeInterfaceFunctions::gJniNativeInterface + : &JniNativeInterfaceFunctions::gJniNativeInterface; +} + +void (*gJniSleepForeverStub[])() = { + nullptr, // reserved0. + nullptr, // reserved1. + nullptr, // reserved2. + nullptr, // reserved3. + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, + SleepForever, +}; + +const JNINativeInterface* GetRuntimeShutdownNativeInterface() { + return reinterpret_cast(&gJniSleepForeverStub); +} + +} // namespace art + +std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs) { + switch (rhs) { + case JNIInvalidRefType: + os << "JNIInvalidRefType"; + return os; + case JNILocalRefType: + os << "JNILocalRefType"; + return os; + case JNIGlobalRefType: + os << "JNIGlobalRefType"; + return os; + case JNIWeakGlobalRefType: + os << "JNIWeakGlobalRefType"; + return os; + default: + LOG(FATAL) << "jobjectRefType[" << static_cast(rhs) << "]"; + UNREACHABLE(); + } +} diff --git a/code/chapter-12/quick_jni_entrypoints.cc b/code/chapter-12/quick_jni_entrypoints.cc new file mode 100644 index 0000000..0eab7c8 --- /dev/null +++ b/code/chapter-12/quick_jni_entrypoints.cc @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "art_method-inl.h" +#include "base/casts.h" +#include "entrypoints/entrypoint_utils-inl.h" +#include "indirect_reference_table.h" +#include "mirror/object-inl.h" +#include "palette/palette.h" +#include "thread-inl.h" +#include "verify_object.h" +#include "utils/Log.h" + +// For methods that monitor JNI invocations and report their begin/end to +// palette hooks. +#define MONITOR_JNI(kind) \ + { \ + bool should_report = false; \ + PaletteShouldReportJniInvocations(&should_report); \ + if (should_report) { \ + kind(self->GetJniEnv()); \ + } \ + } + +namespace art { + +static_assert(sizeof(IRTSegmentState) == sizeof(uint32_t), "IRTSegmentState size unexpected"); +static_assert(std::is_trivial::value, "IRTSegmentState not trivial"); + +static inline void GoToRunnableFast(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + +extern void ReadBarrierJni(mirror::CompressedReference* declaring_class, + Thread* self ATTRIBUTE_UNUSED) { + DCHECK(kUseReadBarrier); + if (kUseBakerReadBarrier) { + DCHECK(declaring_class->AsMirrorPtr() != nullptr) + << "The class of a static jni call must not be null"; + // Check the mark bit and return early if it's already marked. + if (LIKELY(declaring_class->AsMirrorPtr()->GetMarkBit() != 0)) { + return; + } + } + // Call the read barrier and update the handle. + mirror::Class* to_ref = ReadBarrier::BarrierForRoot(declaring_class); + declaring_class->Assign(to_ref); +} + +// Called on entry to fast JNI, push a new local reference table only. +extern uint32_t JniMethodFastStart(Thread* self) { + JNIEnvExt* env = self->GetJniEnv(); + DCHECK(env != nullptr); + uint32_t saved_local_ref_cookie = bit_cast(env->GetLocalRefCookie()); + env->SetLocalRefCookie(env->GetLocalsSegmentState()); + + if (kIsDebugBuild) { + ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + CHECK(native_method->IsFastNative()) << native_method->PrettyMethod(); + } + + return saved_local_ref_cookie; +} + +// Called on entry to JNI, transition out of Runnable and release share of mutator_lock_. +extern uint32_t JniMethodStart(Thread* self) { + JNIEnvExt* env = self->GetJniEnv(); + DCHECK(env != nullptr); + uint32_t saved_local_ref_cookie = bit_cast(env->GetLocalRefCookie()); + env->SetLocalRefCookie(env->GetLocalsSegmentState()); + //add mikrom + Runtime* runtime=Runtime::Current(); + if(runtime->GetConfigItem().isJNIMethodPrint){ + ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + std::string methodname=native_method->PrettyMethod(); + if(strstr(methodname.c_str(),runtime->GetConfigItem().jniFuncName)){ + ALOGD("mikrom enter jni %s %p",methodname.c_str(),self); + runtime->GetConfigItem().jniEnable=true; + } + } + //endadd + if (kIsDebugBuild) { + ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + CHECK(!native_method->IsFastNative()) << native_method->PrettyMethod(); + } + + // Transition out of runnable. + self->TransitionFromRunnableToSuspended(kNative); + return saved_local_ref_cookie; +} + +extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self) { + self->DecodeJObject(to_lock)->MonitorEnter(self); + return JniMethodStart(self); +} + +// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI. +static void GoToRunnable(Thread* self) NO_THREAD_SAFETY_ANALYSIS { + if (kIsDebugBuild) { + ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + CHECK(!native_method->IsFastNative()) << native_method->PrettyMethod(); + } + + self->TransitionFromSuspendedToRunnable(); +} + +ALWAYS_INLINE static inline void GoToRunnableFast(Thread* self) { + if (kIsDebugBuild) { + // Should only enter here if the method is @FastNative. + ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + CHECK(native_method->IsFastNative()) << native_method->PrettyMethod(); + } + + // When we are in @FastNative, we are already Runnable. + // Only do a suspend check on the way out of JNI. + if (UNLIKELY(self->TestAllFlags())) { + // In fast JNI mode we never transitioned out of runnable. Perform a suspend check if there + // is a flag raised. + DCHECK(Locks::mutator_lock_->IsSharedHeld(self)); + self->CheckSuspend(); + } +} + +static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + JNIEnvExt* env = self->GetJniEnv(); + if (UNLIKELY(env->IsCheckJniEnabled())) { + env->CheckNoHeldMonitors(); + } + env->SetLocalSegmentState(env->GetLocalRefCookie()); + env->SetLocalRefCookie(bit_cast(saved_local_ref_cookie)); +} + +// TODO: annotalysis disabled as monitor semantics are maintained in Java code. +static inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) + NO_THREAD_SAFETY_ANALYSIS REQUIRES(!Roles::uninterruptible_) { + // Save any pending exception over monitor exit call. + ObjPtr saved_exception = nullptr; + if (UNLIKELY(self->IsExceptionPending())) { + saved_exception = self->GetException(); + self->ClearException(); + } + // Decode locked object and unlock, before popping local references. + self->DecodeJObject(locked)->MonitorExit(self); + if (UNLIKELY(self->IsExceptionPending())) { + LOG(FATAL) << "Synchronized JNI code returning with an exception:\n" + << saved_exception->Dump() + << "\nEncountered second exception during implicit MonitorExit:\n" + << self->GetException()->Dump(); + } + // Restore pending exception. + if (saved_exception != nullptr) { + self->SetException(saved_exception); + } +} + +// TODO: These should probably be templatized or macro-ized. +// Otherwise there's just too much repetitive boilerplate. + +extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) { + + //add mikrom + Runtime* runtime=Runtime::Current(); + if(runtime->GetConfigItem().isJNIMethodPrint){ + ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + std::string methodname=native_method->PrettyMethod(); + ALOGD("mikrom JniMethodEnd jni %s",methodname.c_str()); + if(strstr(methodname.c_str(),runtime->GetConfigItem().jniFuncName)){ + runtime->GetConfigItem().jniEnable=false; + ALOGD("mikrom leave jni %s",methodname.c_str()); + } + } + //endadd +// ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); +// if(native_method!=nullptr){ +// std::string methodname=native_method->PrettyMethod(); +// ALOGD("mikrom JniMethodEnd %s",methodname.c_str()); +// } + + GoToRunnable(self); + PopLocalReferences(saved_local_ref_cookie, self); +} + +extern void JniMethodFastEnd(uint32_t saved_local_ref_cookie, Thread* self) { +// ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); +// if(native_method!=nullptr){ +// std::string methodname=native_method->PrettyMethod(); +// ALOGD("mikrom JniMethodFastEnd %s",methodname.c_str()); +// } + GoToRunnableFast(self); + PopLocalReferences(saved_local_ref_cookie, self); +} + +extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, + jobject locked, + Thread* self) { +// ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); +// if(native_method!=nullptr){ +// std::string methodname=native_method->PrettyMethod(); +// ALOGD("mikrom JniMethodEndSynchronized %s",methodname.c_str()); +// } + GoToRunnable(self); + UnlockJniSynchronizedMethod(locked, self); // Must decode before pop. + PopLocalReferences(saved_local_ref_cookie, self); +} + +// Common result handling for EndWithReference. +static mirror::Object* JniMethodEndWithReferenceHandleResult(jobject result, + uint32_t saved_local_ref_cookie, + Thread* self) + NO_THREAD_SAFETY_ANALYSIS { + + //add mikrom + Runtime* runtime=Runtime::Current(); + if(runtime->GetConfigItem().isJNIMethodPrint){ + ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + std::string methodname=native_method->PrettyMethod(); + if(strstr(methodname.c_str(),runtime->GetConfigItem().jniFuncName)){ + runtime->GetConfigItem().jniEnable=false; + ALOGD("mikrom leave jni %s",methodname.c_str()); + } + } + //endadd +// ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); +// if(native_method!=nullptr){ +// std::string methodname=native_method->PrettyMethod(); +// ALOGD("mikrom JniMethodEndWithReferenceHandleResult %s",methodname.c_str()); +// } + // Must decode before pop. The 'result' may not be valid in case of an exception, though. + ObjPtr o; + if (!self->IsExceptionPending()) { + o = self->DecodeJObject(result); + } + PopLocalReferences(saved_local_ref_cookie, self); + // Process result. + if (UNLIKELY(self->GetJniEnv()->IsCheckJniEnabled())) { + // CheckReferenceResult can resolve types. + StackHandleScope<1> hs(self); + HandleWrapperObjPtr h_obj(hs.NewHandleWrapper(&o)); + CheckReferenceResult(h_obj, self); + } + VerifyObject(o); + return o.Ptr(); +} + +extern mirror::Object* JniMethodFastEndWithReference(jobject result, + uint32_t saved_local_ref_cookie, + Thread* self) { + GoToRunnableFast(self); + return JniMethodEndWithReferenceHandleResult(result, saved_local_ref_cookie, self); +} + +extern mirror::Object* JniMethodEndWithReference(jobject result, + uint32_t saved_local_ref_cookie, + Thread* self) { + GoToRunnable(self); + return JniMethodEndWithReferenceHandleResult(result, saved_local_ref_cookie, self); +} + +extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result, + uint32_t saved_local_ref_cookie, + jobject locked, + Thread* self) { + GoToRunnable(self); + UnlockJniSynchronizedMethod(locked, self); + return JniMethodEndWithReferenceHandleResult(result, saved_local_ref_cookie, self); +} + +extern uint64_t GenericJniMethodEnd(Thread* self, + uint32_t saved_local_ref_cookie, + jvalue result, + uint64_t result_f, + ArtMethod* called) + // TODO: NO_THREAD_SAFETY_ANALYSIS as GoToRunnable() is NO_THREAD_SAFETY_ANALYSIS + NO_THREAD_SAFETY_ANALYSIS { +// ALOGD("mikrom GenericJniMethodEnd "); + bool critical_native = called->IsCriticalNative(); + bool fast_native = called->IsFastNative(); + bool normal_native = !critical_native && !fast_native; + + // @CriticalNative does not do a state transition. @FastNative usually does not do a state + // transition either but it performs a suspend check that may do state transitions. + if (LIKELY(normal_native)) { + MONITOR_JNI(PaletteNotifyEndJniInvocation); + GoToRunnable(self); + } else if (fast_native) { + GoToRunnableFast(self); + } + // We need the mutator lock (i.e., calling GoToRunnable()) before accessing the shorty or the + // locked object. + if (called->IsSynchronized()) { + DCHECK(normal_native) << "@FastNative/@CriticalNative and synchronize is not supported"; + jobject lock = GetGenericJniSynchronizationObject(self, called); + DCHECK(lock != nullptr); + UnlockJniSynchronizedMethod(lock, self); + } + char return_shorty_char = called->GetShorty()[0]; + if (return_shorty_char == 'L') { + return reinterpret_cast(JniMethodEndWithReferenceHandleResult( + result.l, saved_local_ref_cookie, self)); + } else { + if (LIKELY(!critical_native)) { + PopLocalReferences(saved_local_ref_cookie, self); + } + switch (return_shorty_char) { + case 'F': { + if (kRuntimeISA == InstructionSet::kX86) { + // Convert back the result to float. + double d = bit_cast(result_f); + return bit_cast(static_cast(d)); + } else { + return result_f; + } + } + case 'D': + return result_f; + case 'Z': + return result.z; + case 'B': + return result.b; + case 'C': + return result.c; + case 'S': + return result.s; + case 'I': + return result.i; + case 'J': + return result.j; + case 'V': + return 0; + default: + LOG(FATAL) << "Unexpected return shorty character " << return_shorty_char; + UNREACHABLE(); + } + } +} + +extern uint32_t JniMonitoredMethodStart(Thread* self) { + uint32_t result = JniMethodStart(self); + MONITOR_JNI(PaletteNotifyBeginJniInvocation); + return result; +} + +extern uint32_t JniMonitoredMethodStartSynchronized(jobject to_lock, Thread* self) { + uint32_t result = JniMethodStartSynchronized(to_lock, self); + MONITOR_JNI(PaletteNotifyBeginJniInvocation); + return result; +} + +extern void JniMonitoredMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) { + MONITOR_JNI(PaletteNotifyEndJniInvocation); + return JniMethodEnd(saved_local_ref_cookie, self); +} + +extern void JniMonitoredMethodEndSynchronized(uint32_t saved_local_ref_cookie, + jobject locked, + Thread* self) { + MONITOR_JNI(PaletteNotifyEndJniInvocation); + return JniMethodEndSynchronized(saved_local_ref_cookie, locked, self); +} + +extern mirror::Object* JniMonitoredMethodEndWithReference(jobject result, + uint32_t saved_local_ref_cookie, + Thread* self) { + MONITOR_JNI(PaletteNotifyEndJniInvocation); + return JniMethodEndWithReference(result, saved_local_ref_cookie, self); +} + +extern mirror::Object* JniMonitoredMethodEndWithReferenceSynchronized( + jobject result, + uint32_t saved_local_ref_cookie, + jobject locked, + Thread* self) { + MONITOR_JNI(PaletteNotifyEndJniInvocation); + return JniMethodEndWithReferenceSynchronized(result, saved_local_ref_cookie, locked, self); +} + +} // namespace art diff --git a/code/chapter-12/reflection.cc b/code/chapter-12/reflection.cc new file mode 100644 index 0000000..b367c52 --- /dev/null +++ b/code/chapter-12/reflection.cc @@ -0,0 +1,1258 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "reflection-inl.h" + +#include "art_field-inl.h" +#include "art_method-inl.h" +#include "base/enums.h" +#include "class_linker.h" +#include "common_throws.h" +#include "dex/dex_file-inl.h" +#include "indirect_reference_table-inl.h" +#include "jni/java_vm_ext.h" +#include "jni/jni_internal.h" +#include "jvalue-inl.h" +#include "mirror/class-inl.h" +#include "mirror/executable.h" +#include "mirror/object_array-inl.h" +#include "nativehelper/scoped_local_ref.h" +#include "nth_caller_visitor.h" +#include "scoped_thread_state_change-inl.h" +#include "stack_reference.h" +#include "thread-inl.h" +#include "well_known_classes.h" +#include "utils/Log.h" +#include "link.h" +#include +namespace art { +namespace { + +using android::base::StringPrintf; + +class ArgArray { + public: + ArgArray(const char* shorty, uint32_t shorty_len) + : shorty_(shorty), shorty_len_(shorty_len), num_bytes_(0) { + size_t num_slots = shorty_len + 1; // +1 in case of receiver. + if (LIKELY((num_slots * 2) < kSmallArgArraySize)) { + // We can trivially use the small arg array. + arg_array_ = small_arg_array_; + } else { + // Analyze shorty to see if we need the large arg array. + for (size_t i = 1; i < shorty_len; ++i) { + char c = shorty[i]; + if (c == 'J' || c == 'D') { + num_slots++; + } + } + if (num_slots <= kSmallArgArraySize) { + arg_array_ = small_arg_array_; + } else { + large_arg_array_.reset(new uint32_t[num_slots]); + arg_array_ = large_arg_array_.get(); + } + } + } + + uint32_t* GetArray() { + return arg_array_; + } + + uint32_t GetNumBytes() { + return num_bytes_; + } + + void Append(uint32_t value) { + arg_array_[num_bytes_ / 4] = value; + num_bytes_ += 4; + } + + void Append(ObjPtr obj) REQUIRES_SHARED(Locks::mutator_lock_) { + Append(StackReference::FromMirrorPtr(obj.Ptr()).AsVRegValue()); + } + + void AppendWide(uint64_t value) { + arg_array_[num_bytes_ / 4] = value; + arg_array_[(num_bytes_ / 4) + 1] = value >> 32; + num_bytes_ += 8; + } + + void AppendFloat(float value) { + jvalue jv; + jv.f = value; + Append(jv.i); + } + + void AppendDouble(double value) { + jvalue jv; + jv.d = value; + AppendWide(jv.j); + } + + void BuildArgArrayFromVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + ObjPtr receiver, + va_list ap) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Set receiver if non-null (method is not static) + if (receiver != nullptr) { + Append(receiver); + } + for (size_t i = 1; i < shorty_len_; ++i) { + switch (shorty_[i]) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + Append(va_arg(ap, jint)); + break; + case 'F': + AppendFloat(va_arg(ap, jdouble)); + break; + case 'L': + Append(soa.Decode(va_arg(ap, jobject))); + break; + case 'D': + AppendDouble(va_arg(ap, jdouble)); + break; + case 'J': + AppendWide(va_arg(ap, jlong)); + break; +#ifndef NDEBUG + default: + LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; +#endif + } + } + } + + void BuildArgArrayFromJValues(const ScopedObjectAccessAlreadyRunnable& soa, + ObjPtr receiver, const jvalue* args) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Set receiver if non-null (method is not static) + if (receiver != nullptr) { + Append(receiver); + } + for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) { + switch (shorty_[i]) { + case 'Z': + Append(args[args_offset].z); + break; + case 'B': + Append(args[args_offset].b); + break; + case 'C': + Append(args[args_offset].c); + break; + case 'S': + Append(args[args_offset].s); + break; + case 'I': + FALLTHROUGH_INTENDED; + case 'F': + Append(args[args_offset].i); + break; + case 'L': + Append(soa.Decode(args[args_offset].l)); + break; + case 'D': + FALLTHROUGH_INTENDED; + case 'J': + AppendWide(args[args_offset].j); + break; +#ifndef NDEBUG + default: + LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; +#endif + } + } + } + + void BuildArgArrayFromFrame(ShadowFrame* shadow_frame, uint32_t arg_offset) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Set receiver if non-null (method is not static) + size_t cur_arg = arg_offset; + if (!shadow_frame->GetMethod()->IsStatic()) { + Append(shadow_frame->GetVReg(cur_arg)); + cur_arg++; + } + for (size_t i = 1; i < shorty_len_; ++i) { + switch (shorty_[i]) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + case 'F': + case 'L': + Append(shadow_frame->GetVReg(cur_arg)); + cur_arg++; + break; + case 'D': + case 'J': + AppendWide(shadow_frame->GetVRegLong(cur_arg)); + cur_arg++; + cur_arg++; + break; +#ifndef NDEBUG + default: + LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; +#endif + } + } + } + + + void VarArgsShowArg(const ScopedObjectAccessAlreadyRunnable& soa, + va_list ap) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::stringstream ss; + for (size_t i = 1; i < shorty_len_; ++i) { + switch (shorty_[i]) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + ss<<"mikrom"<<" |: jint : "< receiver =soa.Decode(obj); + if(receiver==nullptr){ + ss<<"mikrom"<<" |: jobject : null\n"; + break; + } + ObjPtr cls=receiver->GetClass(); + if (cls->DescriptorEquals("Ljava/lang/String;")){ + ObjPtr argStr =soa.Decode(obj); + ss<<"mikrom"<<" |: jstring : "<<(const char*)argStr->GetValue()<<"\n"; + }else{ + ss<<"mikrom"<<" |: jobject : "<<&obj<<"\n"; + } + break; + } + case 'D': + ss<<"mikrom"<<" |: jdouble : "< receiver, + ObjPtr> raw_args, + ArtMethod* m, + Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + const dex::TypeList* classes = m->GetParameterTypeList(); + // Set receiver if non-null (method is not static) + if (receiver != nullptr) { + Append(receiver); + } + StackHandleScope<2> hs(self); + MutableHandle arg(hs.NewHandle(nullptr)); + Handle> args( + hs.NewHandle>(raw_args)); + for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) { + arg.Assign(args->Get(args_offset)); + if (((shorty_[i] == 'L') && (arg != nullptr)) || + ((arg == nullptr && shorty_[i] != 'L'))) { + // TODO: The method's parameter's type must have been previously resolved, yet + // we've seen cases where it's not b/34440020. + ObjPtr dst_class( + m->ResolveClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_)); + if (dst_class == nullptr) { + CHECK(self->IsExceptionPending()); + return false; + } + if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) { + ThrowIllegalArgumentException( + StringPrintf("method %s argument %zd has type %s, got %s", + m->PrettyMethod(false).c_str(), + args_offset + 1, // Humans don't count from 0. + mirror::Class::PrettyDescriptor(dst_class).c_str(), + mirror::Object::PrettyTypeOf(arg.Get()).c_str()).c_str()); + return false; + } + } + +#define DO_FIRST_ARG(match_descriptor, get_fn, append) { \ + if (LIKELY(arg != nullptr && \ + arg->GetClass()->DescriptorEquals(match_descriptor))) { \ + ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \ + append(primitive_field-> get_fn(arg.Get())); + +#define DO_ARG(match_descriptor, get_fn, append) \ + } else if (LIKELY(arg != nullptr && \ + arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \ + ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \ + append(primitive_field-> get_fn(arg.Get())); + +#define DO_FAIL(expected) \ + } else { \ + if (arg->GetClass<>()->IsPrimitive()) { \ + std::string temp; \ + ThrowIllegalPrimitiveArgumentException(expected, \ + arg->GetClass<>()->GetDescriptor(&temp)); \ + } else { \ + ThrowIllegalArgumentException(\ + StringPrintf("method %s argument %zd has type %s, got %s", \ + ArtMethod::PrettyMethod(m, false).c_str(), \ + args_offset + 1, \ + expected, \ + mirror::Object::PrettyTypeOf(arg.Get()).c_str()).c_str()); \ + } \ + return false; \ + } } + + switch (shorty_[i]) { + case 'L': + Append(arg.Get()); + break; + case 'Z': + DO_FIRST_ARG("Ljava/lang/Boolean;", GetBoolean, Append) + DO_FAIL("boolean") + break; + case 'B': + DO_FIRST_ARG("Ljava/lang/Byte;", GetByte, Append) + DO_FAIL("byte") + break; + case 'C': + DO_FIRST_ARG("Ljava/lang/Character;", GetChar, Append) + DO_FAIL("char") + break; + case 'S': + DO_FIRST_ARG("Ljava/lang/Short;", GetShort, Append) + DO_ARG("Ljava/lang/Byte;", GetByte, Append) + DO_FAIL("short") + break; + case 'I': + DO_FIRST_ARG("Ljava/lang/Integer;", GetInt, Append) + DO_ARG("Ljava/lang/Character;", GetChar, Append) + DO_ARG("Ljava/lang/Short;", GetShort, Append) + DO_ARG("Ljava/lang/Byte;", GetByte, Append) + DO_FAIL("int") + break; + case 'J': + DO_FIRST_ARG("Ljava/lang/Long;", GetLong, AppendWide) + DO_ARG("Ljava/lang/Integer;", GetInt, AppendWide) + DO_ARG("Ljava/lang/Character;", GetChar, AppendWide) + DO_ARG("Ljava/lang/Short;", GetShort, AppendWide) + DO_ARG("Ljava/lang/Byte;", GetByte, AppendWide) + DO_FAIL("long") + break; + case 'F': + DO_FIRST_ARG("Ljava/lang/Float;", GetFloat, AppendFloat) + DO_ARG("Ljava/lang/Long;", GetLong, AppendFloat) + DO_ARG("Ljava/lang/Integer;", GetInt, AppendFloat) + DO_ARG("Ljava/lang/Character;", GetChar, AppendFloat) + DO_ARG("Ljava/lang/Short;", GetShort, AppendFloat) + DO_ARG("Ljava/lang/Byte;", GetByte, AppendFloat) + DO_FAIL("float") + break; + case 'D': + DO_FIRST_ARG("Ljava/lang/Double;", GetDouble, AppendDouble) + DO_ARG("Ljava/lang/Float;", GetFloat, AppendDouble) + DO_ARG("Ljava/lang/Long;", GetLong, AppendDouble) + DO_ARG("Ljava/lang/Integer;", GetInt, AppendDouble) + DO_ARG("Ljava/lang/Character;", GetChar, AppendDouble) + DO_ARG("Ljava/lang/Short;", GetShort, AppendDouble) + DO_ARG("Ljava/lang/Byte;", GetByte, AppendDouble) + DO_FAIL("double") + break; +#ifndef NDEBUG + default: + LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; + UNREACHABLE(); +#endif + } +#undef DO_FIRST_ARG +#undef DO_ARG +#undef DO_FAIL + } + return true; + } + + private: + enum { kSmallArgArraySize = 16 }; + const char* const shorty_; + const uint32_t shorty_len_; + uint32_t num_bytes_; + uint32_t* arg_array_; + uint32_t small_arg_array_[kSmallArgArraySize]; + std::unique_ptr large_arg_array_; +}; + +void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args) + REQUIRES_SHARED(Locks::mutator_lock_) { + const dex::TypeList* params = m->GetParameterTypeList(); + if (params == nullptr) { + return; // No arguments so nothing to check. + } + uint32_t offset = 0; + uint32_t num_params = params->Size(); + size_t error_count = 0; + if (!m->IsStatic()) { + offset = 1; + } + // TODO: If args contain object references, it may cause problems. + Thread* const self = Thread::Current(); + for (uint32_t i = 0; i < num_params; i++) { + dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_; + ObjPtr param_type(m->ResolveClassFromTypeIndex(type_idx)); + if (param_type == nullptr) { + CHECK(self->IsExceptionPending()); + LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: " + << m->GetTypeDescriptorFromTypeIdx(type_idx) << "\n" + << self->GetException()->Dump(); + self->ClearException(); + ++error_count; + } else if (!param_type->IsPrimitive()) { + // TODO: There is a compaction bug here since GetClassFromTypeIdx can cause thread suspension, + // this is a hard to fix problem since the args can contain Object*, we need to save and + // restore them by using a visitor similar to the ones used in the trampoline entrypoints. + ObjPtr argument = + (reinterpret_cast*>(&args[i + offset]))->AsMirrorPtr(); + if (argument != nullptr && !argument->InstanceOf(param_type)) { + LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of " + << argument->PrettyTypeOf() << " as argument " << (i + 1) + << " to " << m->PrettyMethod(); + ++error_count; + } + } else if (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble()) { + offset++; + } else { + int32_t arg = static_cast(args[i + offset]); + if (param_type->IsPrimitiveBoolean()) { + if (arg != JNI_TRUE && arg != JNI_FALSE) { + LOG(ERROR) << "JNI ERROR (app bug): expected jboolean (0/1) but got value of " + << arg << " as argument " << (i + 1) << " to " << m->PrettyMethod(); + ++error_count; + } + } else if (param_type->IsPrimitiveByte()) { + if (arg < -128 || arg > 127) { + LOG(ERROR) << "JNI ERROR (app bug): expected jbyte but got value of " + << arg << " as argument " << (i + 1) << " to " << m->PrettyMethod(); + ++error_count; + } + } else if (param_type->IsPrimitiveChar()) { + if (args[i + offset] > 0xFFFF) { + LOG(ERROR) << "JNI ERROR (app bug): expected jchar but got value of " + << arg << " as argument " << (i + 1) << " to " << m->PrettyMethod(); + ++error_count; + } + } else if (param_type->IsPrimitiveShort()) { + if (arg < -32768 || arg > 0x7FFF) { + LOG(ERROR) << "JNI ERROR (app bug): expected jshort but got value of " + << arg << " as argument " << (i + 1) << " to " << m->PrettyMethod(); + ++error_count; + } + } + } + } + if (UNLIKELY(error_count > 0)) { + // TODO: pass the JNI function name (such as "CallVoidMethodV") through so we can call JniAbort + // with an argument. + vm->JniAbortF(nullptr, "bad arguments passed to %s (see above for details)", + m->PrettyMethod().c_str()); + } +} + +ArtMethod* FindVirtualMethod(ObjPtr receiver, ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_) { + return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method, kRuntimePointerSize); +} + + +void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* method, ArgArray* arg_array, JValue* result, + const char* shorty) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t* args = arg_array->GetArray(); + if (UNLIKELY(soa.Env()->IsCheckJniEnabled())) { + CheckMethodArguments(soa.Vm(), method->GetInterfaceMethodIfProxy(kRuntimePointerSize), args); + } + method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty); +} + +ALWAYS_INLINE +bool CheckArgsForInvokeMethod(ArtMethod* np_method, + ObjPtr> objects) + REQUIRES_SHARED(Locks::mutator_lock_) { + const dex::TypeList* classes = np_method->GetParameterTypeList(); + uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size(); + uint32_t arg_count = (objects == nullptr) ? 0 : objects->GetLength(); + if (UNLIKELY(arg_count != classes_size)) { + ThrowIllegalArgumentException(StringPrintf("Wrong number of arguments; expected %d, got %d", + classes_size, arg_count).c_str()); + return false; + } + return true; +} + +ALWAYS_INLINE +bool InvokeMethodImpl(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* m, + ArtMethod* np_method, + ObjPtr receiver, + ObjPtr> objects, + const char** shorty, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { + // Invoke the method. + uint32_t shorty_len = 0; + *shorty = np_method->GetShorty(&shorty_len); + ArgArray arg_array(*shorty, shorty_len); + if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method, soa.Self())) { + CHECK(soa.Self()->IsExceptionPending()); + return false; + } + + InvokeWithArgArray(soa, m, &arg_array, result, *shorty); + + // Wrap any exception with "Ljava/lang/reflect/InvocationTargetException;" and return early. + if (soa.Self()->IsExceptionPending()) { + // To abort a transaction we use a fake exception that should never be caught by the bytecode + // and therefore it makes no sense to wrap it. + if (Runtime::Current()->IsTransactionAborted()) { + DCHECK(soa.Self()->GetException()->GetClass()->DescriptorEquals( + "Ldalvik/system/TransactionAbortError;")) + << soa.Self()->GetException()->GetClass()->PrettyDescriptor(); + } else { + // If we get another exception when we are trying to wrap, then just use that instead. + ScopedLocalRef th(soa.Env(), soa.Env()->ExceptionOccurred()); + soa.Self()->ClearException(); + jobject exception_instance = + soa.Env()->NewObject(WellKnownClasses::java_lang_reflect_InvocationTargetException, + WellKnownClasses::java_lang_reflect_InvocationTargetException_init, + th.get()); + if (exception_instance == nullptr) { + soa.Self()->AssertPendingException(); + return false; + } + soa.Env()->Throw(reinterpret_cast(exception_instance)); + } + return false; + } + + return true; +} + +} // anonymous namespace + +template <> +JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + ArtMethod* method, + va_list args) REQUIRES_SHARED(Locks::mutator_lock_) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { + ThrowStackOverflowError(soa.Self()); + return JValue(); + } + bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor(); + if (is_string_init) { + // Replace calls to String. with equivalent StringFactory call. + method = WellKnownClasses::StringInitToStringFactory(method); + } + ObjPtr receiver = method->IsStatic() ? nullptr : soa.Decode(obj); + uint32_t shorty_len = 0; + const char* shorty = + method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len); + JValue result; + ArgArray arg_array(shorty, shorty_len); + arg_array.BuildArgArrayFromVarArgs(soa, receiver, args); + InvokeWithArgArray(soa, method, &arg_array, &result, shorty); + if (is_string_init) { + // For string init, remap original receiver to StringFactory result. + UpdateReference(soa.Self(), obj, result.GetL()); + } + return result; +} + +template <> +JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + jmethodID mid, + va_list args) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(mid != nullptr) << "Called with null jmethodID"; + return InvokeWithVarArgs(soa, obj, jni::DecodeArtMethod(mid), args); +} + +template <> +JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + ArtMethod* method, + const jvalue* args) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { + ThrowStackOverflowError(soa.Self()); + return JValue(); + } + bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor(); + if (is_string_init) { + // Replace calls to String. with equivalent StringFactory call. + method = WellKnownClasses::StringInitToStringFactory(method); + } + ObjPtr receiver = method->IsStatic() ? nullptr : soa.Decode(obj); + uint32_t shorty_len = 0; + const char* shorty = + method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len); + JValue result; + ArgArray arg_array(shorty, shorty_len); + arg_array.BuildArgArrayFromJValues(soa, receiver, args); + InvokeWithArgArray(soa, method, &arg_array, &result, shorty); + if (is_string_init) { + // For string init, remap original receiver to StringFactory result. + UpdateReference(soa.Self(), obj, result.GetL()); + } + return result; +} + +template <> +JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + jmethodID mid, + const jvalue* args) { + DCHECK(mid != nullptr) << "Called with null jmethodID"; + return InvokeWithJValues(soa, obj, jni::DecodeArtMethod(mid), args); +} + +template <> +JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + ArtMethod* interface_method, + const jvalue* args) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { + ThrowStackOverflowError(soa.Self()); + return JValue(); + } + ObjPtr receiver = soa.Decode(obj); + ArtMethod* method = FindVirtualMethod(receiver, interface_method); + bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor(); + if (is_string_init) { + // Replace calls to String. with equivalent StringFactory call. + method = WellKnownClasses::StringInitToStringFactory(method); + receiver = nullptr; + } + uint32_t shorty_len = 0; + const char* shorty = + method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len); + JValue result; + ArgArray arg_array(shorty, shorty_len); + arg_array.BuildArgArrayFromJValues(soa, receiver, args); + InvokeWithArgArray(soa, method, &arg_array, &result, shorty); + if (is_string_init) { + // For string init, remap original receiver to StringFactory result. + UpdateReference(soa.Self(), obj, result.GetL()); + } + return result; +} + +template <> +JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + jmethodID mid, + const jvalue* args) { + DCHECK(mid != nullptr) << "Called with null jmethodID"; + return InvokeVirtualOrInterfaceWithJValues(soa, obj, jni::DecodeArtMethod(mid), args); +} + +template <> +JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + ArtMethod* interface_method, + va_list args) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { + ThrowStackOverflowError(soa.Self()); + return JValue(); + } + + ObjPtr receiver = soa.Decode(obj); + ArtMethod* method = FindVirtualMethod(receiver, interface_method); + bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor(); + if (is_string_init) { + // Replace calls to String. with equivalent StringFactory call. + method = WellKnownClasses::StringInitToStringFactory(method); + receiver = nullptr; + } + uint32_t shorty_len = 0; + const char* shorty = + method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len); + JValue result; + ArgArray arg_array(shorty, shorty_len); + arg_array.BuildArgArrayFromVarArgs(soa, receiver, args); + InvokeWithArgArray(soa, method, &arg_array, &result, shorty); + if (is_string_init) { + // For string init, remap original receiver to StringFactory result. + UpdateReference(soa.Self(), obj, result.GetL()); + } + return result; +} + +template <> +JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + jmethodID mid, + va_list args) { + DCHECK(mid != nullptr) << "Called with null jmethodID"; + return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, jni::DecodeArtMethod(mid), args); +} + +template +jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod, + jobject javaReceiver, jobject javaArgs, size_t num_frames) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < + soa.Self()->GetStackEndForInterpreter(true))) { + ThrowStackOverflowError(soa.Self()); + return nullptr; + } + + ObjPtr executable = soa.Decode(javaMethod); + const bool accessible = executable->IsAccessible(); + ArtMethod* m = executable->GetArtMethod(); + + ObjPtr declaring_class = m->GetDeclaringClass(); + if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) { + Thread* self = soa.Self(); + StackHandleScope<1> hs(self); + HandleWrapperObjPtr h_class(hs.NewHandleWrapper(&declaring_class)); + if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized( + self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + DCHECK(h_class->IsInitializing()); + } + + ObjPtr receiver; + if (!m->IsStatic()) { + // Replace calls to String. with equivalent StringFactory call. + if (declaring_class->IsStringClass() && m->IsConstructor()) { + m = WellKnownClasses::StringInitToStringFactory(m); + CHECK(javaReceiver == nullptr); + } else { + // Check that the receiver is non-null and an instance of the field's declaring class. + receiver = soa.Decode(javaReceiver); + if (!VerifyObjectIsClass(receiver, declaring_class)) { + return nullptr; + } + + // Find the actual implementation of the virtual method. + m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m, kPointerSize); + } + } + + // Get our arrays of arguments and their types, and check they're the same size. + ObjPtr> objects = + soa.Decode>(javaArgs); + auto* np_method = m->GetInterfaceMethodIfProxy(kPointerSize); + if (!CheckArgsForInvokeMethod(np_method, objects)) { + return nullptr; + } + + // If method is not set to be accessible, verify it can be accessed by the caller. + ObjPtr calling_class; + if (!accessible && !VerifyAccess(soa.Self(), + receiver, + declaring_class, + m->GetAccessFlags(), + &calling_class, + num_frames)) { + ThrowIllegalAccessException( + StringPrintf("Class %s cannot access %s method %s of class %s", + calling_class == nullptr ? "null" : calling_class->PrettyClass().c_str(), + PrettyJavaAccessFlags(m->GetAccessFlags()).c_str(), + m->PrettyMethod().c_str(), + m->GetDeclaringClass() == nullptr ? "null" : + m->GetDeclaringClass()->PrettyClass().c_str()).c_str()); + return nullptr; + } + + // Invoke the method. + JValue result; + const char* shorty; + if (!InvokeMethodImpl(soa, m, np_method, receiver, objects, &shorty, &result)) { + return nullptr; + } + return soa.AddLocalReference(BoxPrimitive(Primitive::GetType(shorty[0]), result)); +} + +template +jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, + jobject javaMethod, + jobject javaReceiver, + jobject javaArgs, + size_t num_frames); +template +jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, + jobject javaMethod, + jobject javaReceiver, + jobject javaArgs, + size_t num_frames); + +bool HasShow(){ + Runtime* runtime=Runtime::Current(); + if(!runtime->GetConfigItem().isJNIMethodPrint ||!runtime->GetConfigItem().jniEnable){ + return false; + } + return true; +} + +typedef const char* (*kbacktraceFunc)(bool,const char*); + +const char* getBacktrace(const char* moduleName){ + Runtime* runtime=Runtime::Current(); + if(runtime->GetConfigItem().kbacktrace== nullptr){ + ALOGD("mikrom kbacktrace is null"); + return nullptr; + } + kbacktraceFunc kbacktrace=(kbacktraceFunc)runtime->GetConfigItem().kbacktrace; + return kbacktrace(true,moduleName); +} + + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,const char* funcname,jclass java_class, const char* name, const char* sig,jmethodID methodID){ + if(!HasShow()){ + return; + } + ObjPtr c = soa.Decode(java_class); + std::string temp; + const char* className= c->GetDescriptor(&temp); + ArtMethod* method = jni::DecodeArtMethod(methodID); + pid_t pid = getpid(); + ALOGD("%s /* TID %d */","mikrom",pid); + ALOGD("%s [+] JNIEnv->%s","mikrom",funcname); + ALOGD("%s |- jclass :%s","mikrom",className); + ALOGD("%s |- char* :%p","mikrom",name); + ALOGD("%s |: %s","mikrom",name); + ALOGD("%s |- char* :%p","mikrom",sig); + ALOGD("%s |: %s","mikrom",sig); + ALOGD("%s |= jmethodID :0x%x {%s}","mikrom",method->GetMethodIndex(),method->PrettyMethod().c_str()); + Runtime* runtime=Runtime::Current(); + const char* backtrace= getBacktrace(runtime->GetConfigItem().jniModuleName); + ALOGD("-------------------------mikrom Backtrace-------------------------\n%s\n",backtrace); +} + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& , + const char* funcname, + const char* data ){ + if(!HasShow()){ + return; + } + pid_t pid = getpid(); + ALOGD("%s /* TID %d */","mikrom",pid); + ALOGD("%s [+] JNIEnv->%s","mikrom",funcname); + ALOGD("%s |- char* : %s","mikrom",data); + Runtime* runtime=Runtime::Current(); + const char* backtrace= getBacktrace(runtime->GetConfigItem().jniModuleName); + ALOGD("-------------------------mikrom Backtrace-------------------------\n%s\n",backtrace); +} + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& , + const char* funcname, + jboolean* is_copy , + const char* data ){ + if(!HasShow()){ + return; + } + + pid_t pid = getpid(); + ALOGD("%s /* TID %d */","mikrom",pid); + ALOGD("%s [+] JNIEnv->%s","mikrom",funcname); + if(is_copy== nullptr){ + ALOGD("%s |- jboolean* : %d","mikrom",false); + }else{ + ALOGD("%s |- jboolean* : %d","mikrom",*is_copy); + } + ALOGD("%s |= char* : %s","mikrom",data); + Runtime* runtime=Runtime::Current(); + const char* backtrace= getBacktrace(runtime->GetConfigItem().jniModuleName); + ALOGD("-------------------------mikrom Backtrace-------------------------\n%s\n",backtrace); +} + + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + const char* funcname, + jmethodID mid, + va_list vaList){ + if(!HasShow()){ + return; + } + + ArtMethod* method = jni::DecodeArtMethod(mid); + pid_t pid = getpid(); + ALOGD("%s /* TID %d */","mikrom",pid); + ALOGD("%s [+] JNIEnv->%s","mikrom",funcname); + ALOGD("%s |- jmethodID :0x%x {%s}","mikrom",method->GetMethodIndex(),method->PrettyMethod().c_str()); + ALOGD("%s |- va_list :%p","mikrom",&vaList); + + uint32_t shorty_len = 0; + const char* shorty = + method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len); + ArgArray arg_array(shorty, shorty_len); + arg_array.VarArgsShowArg(soa, vaList); +} + + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + const char* funcname, + jmethodID mid, + va_list valist, + jobject ret){ + if(!HasShow()){ + return; + } + ShowVarArgs(soa,funcname,mid,valist); + + ObjPtr receiver =soa.Decode(ret); + if(receiver==nullptr){ + return; + } + ObjPtr cls=receiver->GetClass(); + if (cls->DescriptorEquals("Ljava/lang/String;")){ + ObjPtr retStr =soa.Decode(ret); + ALOGD("%s |= jstring :%s","mikrom",(const char*)retStr->GetValue()); + }else{ + std::string temp; + const char* className= cls->GetDescriptor(&temp); + ALOGD("%s |= jobject :%p {%s}","mikrom",&ret,className); + } + Runtime* runtime=Runtime::Current(); + const char* backtrace= getBacktrace(runtime->GetConfigItem().jniModuleName); + ALOGD("-------------------------mikrom Backtrace-------------------------\n%s\n",backtrace); +} + +void InvokeConstructor(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* constructor, + ObjPtr receiver, + jobject javaArgs) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEndForInterpreter(true))) { + ThrowStackOverflowError(soa.Self()); + return; + } + + if (kIsDebugBuild) { + CHECK(constructor->IsConstructor()); + + ObjPtr declaring_class = constructor->GetDeclaringClass(); + CHECK(declaring_class->IsInitializing()); + + // Calls to String. should have been repplaced with with equivalent StringFactory calls. + CHECK(!declaring_class->IsStringClass()); + + // Check that the receiver is non-null and an instance of the field's declaring class. + CHECK(receiver != nullptr); + CHECK(VerifyObjectIsClass(receiver, declaring_class)); + CHECK_EQ(constructor, + receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(constructor, + kRuntimePointerSize)); + } + + // Get our arrays of arguments and their types, and check they're the same size. + ObjPtr> objects = + soa.Decode>(javaArgs); + ArtMethod* np_method = constructor->GetInterfaceMethodIfProxy(kRuntimePointerSize); + if (!CheckArgsForInvokeMethod(np_method, objects)) { + return; + } + + // Invoke the constructor. + JValue result; + const char* shorty; + InvokeMethodImpl(soa, constructor, np_method, receiver, objects, &shorty, &result); +} + +ObjPtr BoxPrimitive(Primitive::Type src_class, const JValue& value) { + if (src_class == Primitive::kPrimNot) { + return value.GetL(); + } + if (src_class == Primitive::kPrimVoid) { + // There's no such thing as a void field, and void methods invoked via reflection return null. + return nullptr; + } + + jmethodID m = nullptr; + const char* shorty; + switch (src_class) { + case Primitive::kPrimBoolean: + m = WellKnownClasses::java_lang_Boolean_valueOf; + shorty = "LZ"; + break; + case Primitive::kPrimByte: + m = WellKnownClasses::java_lang_Byte_valueOf; + shorty = "LB"; + break; + case Primitive::kPrimChar: + m = WellKnownClasses::java_lang_Character_valueOf; + shorty = "LC"; + break; + case Primitive::kPrimDouble: + m = WellKnownClasses::java_lang_Double_valueOf; + shorty = "LD"; + break; + case Primitive::kPrimFloat: + m = WellKnownClasses::java_lang_Float_valueOf; + shorty = "LF"; + break; + case Primitive::kPrimInt: + m = WellKnownClasses::java_lang_Integer_valueOf; + shorty = "LI"; + break; + case Primitive::kPrimLong: + m = WellKnownClasses::java_lang_Long_valueOf; + shorty = "LJ"; + break; + case Primitive::kPrimShort: + m = WellKnownClasses::java_lang_Short_valueOf; + shorty = "LS"; + break; + default: + LOG(FATAL) << static_cast(src_class); + shorty = nullptr; + } + + ScopedObjectAccessUnchecked soa(Thread::Current()); + DCHECK_EQ(soa.Self()->GetState(), kRunnable); + + ArgArray arg_array(shorty, 2); + JValue result; + if (src_class == Primitive::kPrimDouble || src_class == Primitive::kPrimLong) { + arg_array.AppendWide(value.GetJ()); + } else { + arg_array.Append(value.GetI()); + } + + jni::DecodeArtMethod(m)->Invoke(soa.Self(), + arg_array.GetArray(), + arg_array.GetNumBytes(), + &result, + shorty); + return result.GetL(); +} + +static std::string UnboxingFailureKind(ArtField* f) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (f != nullptr) { + return "field " + f->PrettyField(false); + } + return "result"; +} + +static bool UnboxPrimitive(ObjPtr o, + ObjPtr dst_class, + ArtField* f, + JValue* unboxed_value) + REQUIRES_SHARED(Locks::mutator_lock_) { + bool unbox_for_result = (f == nullptr); + if (!dst_class->IsPrimitive()) { + if (UNLIKELY(o != nullptr && !o->InstanceOf(dst_class))) { + if (!unbox_for_result) { + ThrowIllegalArgumentException( + StringPrintf("%s has type %s, got %s", + UnboxingFailureKind(f).c_str(), + dst_class->PrettyDescriptor().c_str(), + o->PrettyTypeOf().c_str()).c_str()); + } else { + ThrowClassCastException( + StringPrintf("Couldn't convert result of type %s to %s", + o->PrettyTypeOf().c_str(), + dst_class->PrettyDescriptor().c_str()).c_str()); + } + return false; + } + unboxed_value->SetL(o); + return true; + } + if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) { + ThrowIllegalArgumentException(StringPrintf("Can't unbox %s to void", + UnboxingFailureKind(f).c_str()).c_str()); + return false; + } + if (UNLIKELY(o == nullptr)) { + if (!unbox_for_result) { + ThrowIllegalArgumentException( + StringPrintf("%s has type %s, got null", + UnboxingFailureKind(f).c_str(), + dst_class->PrettyDescriptor().c_str()).c_str()); + } else { + ThrowNullPointerException( + StringPrintf("Expected to unbox a '%s' primitive type but was returned null", + dst_class->PrettyDescriptor().c_str()).c_str()); + } + return false; + } + + JValue boxed_value; + ObjPtr klass = o->GetClass(); + Primitive::Type primitive_type; + ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0); + if (klass->DescriptorEquals("Ljava/lang/Boolean;")) { + primitive_type = Primitive::kPrimBoolean; + boxed_value.SetZ(primitive_field->GetBoolean(o)); + } else if (klass->DescriptorEquals("Ljava/lang/Byte;")) { + primitive_type = Primitive::kPrimByte; + boxed_value.SetB(primitive_field->GetByte(o)); + } else if (klass->DescriptorEquals("Ljava/lang/Character;")) { + primitive_type = Primitive::kPrimChar; + boxed_value.SetC(primitive_field->GetChar(o)); + } else if (klass->DescriptorEquals("Ljava/lang/Float;")) { + primitive_type = Primitive::kPrimFloat; + boxed_value.SetF(primitive_field->GetFloat(o)); + } else if (klass->DescriptorEquals("Ljava/lang/Double;")) { + primitive_type = Primitive::kPrimDouble; + boxed_value.SetD(primitive_field->GetDouble(o)); + } else if (klass->DescriptorEquals("Ljava/lang/Integer;")) { + primitive_type = Primitive::kPrimInt; + boxed_value.SetI(primitive_field->GetInt(o)); + } else if (klass->DescriptorEquals("Ljava/lang/Long;")) { + primitive_type = Primitive::kPrimLong; + boxed_value.SetJ(primitive_field->GetLong(o)); + } else if (klass->DescriptorEquals("Ljava/lang/Short;")) { + primitive_type = Primitive::kPrimShort; + boxed_value.SetS(primitive_field->GetShort(o)); + } else { + std::string temp; + ThrowIllegalArgumentException( + StringPrintf("%s has type %s, got %s", UnboxingFailureKind(f).c_str(), + dst_class->PrettyDescriptor().c_str(), + PrettyDescriptor(o->GetClass()->GetDescriptor(&temp)).c_str()).c_str()); + return false; + } + + return ConvertPrimitiveValue(unbox_for_result, + primitive_type, + dst_class->GetPrimitiveType(), + boxed_value, unboxed_value); +} + +bool UnboxPrimitiveForField(ObjPtr o, + ObjPtr dst_class, + ArtField* f, + JValue* unboxed_value) { + DCHECK(f != nullptr); + return UnboxPrimitive(o, dst_class, f, unboxed_value); +} + +bool UnboxPrimitiveForResult(ObjPtr o, + ObjPtr dst_class, + JValue* unboxed_value) { + return UnboxPrimitive(o, dst_class, nullptr, unboxed_value); +} + +ObjPtr GetCallingClass(Thread* self, size_t num_frames) { + NthCallerVisitor visitor(self, num_frames); + visitor.WalkStack(); + return visitor.caller != nullptr ? visitor.caller->GetDeclaringClass() : nullptr; +} + +bool VerifyAccess(Thread* self, + ObjPtr obj, + ObjPtr declaring_class, + uint32_t access_flags, + ObjPtr* calling_class, + size_t num_frames) { + if ((access_flags & kAccPublic) != 0) { + return true; + } + ObjPtr klass = GetCallingClass(self, num_frames); + if (UNLIKELY(klass == nullptr)) { + // The caller is an attached native thread. + return false; + } + *calling_class = klass; + return VerifyAccess(obj, declaring_class, access_flags, klass); +} + +bool VerifyAccess(ObjPtr obj, + ObjPtr declaring_class, + uint32_t access_flags, + ObjPtr calling_class) { + if (calling_class == declaring_class) { + return true; + } + ScopedAssertNoThreadSuspension sants("verify-access"); + if ((access_flags & kAccPrivate) != 0) { + return false; + } + if ((access_flags & kAccProtected) != 0) { + if (obj != nullptr && !obj->InstanceOf(calling_class) && + !declaring_class->IsInSamePackage(calling_class)) { + return false; + } else if (declaring_class->IsAssignableFrom(calling_class)) { + return true; + } + } + return declaring_class->IsInSamePackage(calling_class); +} + +void InvalidReceiverError(ObjPtr o, ObjPtr c) { + std::string expected_class_name(mirror::Class::PrettyDescriptor(c)); + std::string actual_class_name(mirror::Object::PrettyTypeOf(o)); + ThrowIllegalArgumentException(StringPrintf("Expected receiver of type %s, but got %s", + expected_class_name.c_str(), + actual_class_name.c_str()).c_str()); +} + +// This only works if there's one reference which points to the object in obj. +// Will need to be fixed if there's cases where it's not. +void UpdateReference(Thread* self, jobject obj, ObjPtr result) { + IndirectRef ref = reinterpret_cast(obj); + IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref); + if (kind == kLocal) { + self->GetJniEnv()->UpdateLocal(obj, result); + } else if (kind == kJniTransitionOrInvalid) { + LOG(FATAL) << "Unsupported UpdateReference for kind kJniTransitionOrInvalid"; + } else if (kind == kGlobal) { + self->GetJniEnv()->GetVm()->UpdateGlobal(self, ref, result); + } else { + DCHECK_EQ(kind, kWeakGlobal); + self->GetJniEnv()->GetVm()->UpdateWeakGlobal(self, ref, result); + } +} + +} // namespace art diff --git a/code/chapter-12/reflection.h b/code/chapter-12/reflection.h new file mode 100644 index 0000000..91fb486 --- /dev/null +++ b/code/chapter-12/reflection.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_REFLECTION_H_ +#define ART_RUNTIME_REFLECTION_H_ + +#include "base/enums.h" +#include "base/locks.h" +#include "dex/primitive.h" +#include "jni.h" +#include "obj_ptr.h" + +namespace art { +namespace mirror { +class Class; +class Object; +} // namespace mirror +class ArtField; +class ArtMethod; +union JValue; +class ScopedObjectAccessAlreadyRunnable; +class ShadowFrame; + +ObjPtr BoxPrimitive(Primitive::Type src_class, const JValue& value) + REQUIRES_SHARED(Locks::mutator_lock_); + +bool UnboxPrimitiveForField(ObjPtr o, + ObjPtr dst_class, + ArtField* f, + JValue* unboxed_value) + REQUIRES_SHARED(Locks::mutator_lock_); + +bool UnboxPrimitiveForResult(ObjPtr o, + ObjPtr dst_class, + JValue* unboxed_value) + REQUIRES_SHARED(Locks::mutator_lock_); + +ALWAYS_INLINE bool ConvertPrimitiveValueNoThrow(Primitive::Type src_class, + Primitive::Type dst_class, + const JValue& src, + JValue* dst) + REQUIRES_SHARED(Locks::mutator_lock_); + +ALWAYS_INLINE bool ConvertPrimitiveValue(bool unbox_for_result, + Primitive::Type src_class, + Primitive::Type dst_class, + const JValue& src, + JValue* dst) + REQUIRES_SHARED(Locks::mutator_lock_); + +// Invokes the given method (either an ArtMethod or a jmethodID) with direct/static semantics. +template +JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + MethodType mid, + va_list args) + REQUIRES_SHARED(Locks::mutator_lock_); + +// Invokes the given method (either an ArtMethod or a jmethodID) with reflection semantics. +template +JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + MethodType mid, + const jvalue* args) + REQUIRES_SHARED(Locks::mutator_lock_); + +// Invokes the given method (either an ArtMethod or a jmethodID) with virtual/interface semantics. +// Note this will perform lookup based on the 'obj' to determine which implementation of the given +// method should be invoked. +template +JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + MethodType mid, + const jvalue* args) + REQUIRES_SHARED(Locks::mutator_lock_); + +// Invokes the given method (either an ArtMethod or a jmethodID) with virtual/interface semantics. +// Note this will perform lookup based on the 'obj' to determine which implementation of the given +// method should be invoked. +template +JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + jobject obj, + MethodType mid, + va_list args) + REQUIRES_SHARED(Locks::mutator_lock_); + +// num_frames is number of frames we look up for access check. +template +jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, + jobject method, + jobject receiver, + jobject args, + size_t num_frames = 1) + REQUIRES_SHARED(Locks::mutator_lock_); + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& , + const char* funcname, + const char* data ) + REQUIRES_SHARED(Locks::mutator_lock_); + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& , + const char* funcname, + jboolean* is_copy , + const char* data ) + REQUIRES_SHARED(Locks::mutator_lock_); + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,const char* funcname, + jclass java_class, const char* name, const char* sig,jmethodID methodID) + REQUIRES_SHARED(Locks::mutator_lock_); + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + const char* funcname, + jmethodID mid, + va_list valist) + REQUIRES_SHARED(Locks::mutator_lock_); + +void ShowVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, + const char* funcname, + jmethodID mid, + va_list valist, + jobject ret) +REQUIRES_SHARED(Locks::mutator_lock_); + + + +// Special-casing of the above. Assumes that the method is the correct constructor, the class is +// initialized, and that the receiver is an instance of the class. +void InvokeConstructor(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* constructor, + ObjPtr receiver, + jobject args) + REQUIRES_SHARED(Locks::mutator_lock_); + +ALWAYS_INLINE bool VerifyObjectIsClass(ObjPtr o, ObjPtr c) + REQUIRES_SHARED(Locks::mutator_lock_); + +bool VerifyAccess(Thread* self, + ObjPtr obj, + ObjPtr declaring_class, + uint32_t access_flags, + ObjPtr* calling_class, + size_t num_frames) + REQUIRES_SHARED(Locks::mutator_lock_); + +// This version takes a known calling class. +bool VerifyAccess(ObjPtr obj, + ObjPtr declaring_class, + uint32_t access_flags, + ObjPtr calling_class) + REQUIRES_SHARED(Locks::mutator_lock_); + +// Get the calling class by using a stack visitor, may return null for unattached native threads. +ObjPtr GetCallingClass(Thread* self, size_t num_frames) + REQUIRES_SHARED(Locks::mutator_lock_); + +void InvalidReceiverError(ObjPtr o, ObjPtr c) + REQUIRES_SHARED(Locks::mutator_lock_); + +void UpdateReference(Thread* self, jobject obj, ObjPtr result) + REQUIRES_SHARED(Locks::mutator_lock_); + +} // namespace art + +#endif // ART_RUNTIME_REFLECTION_H_ diff --git a/code/chapter-12/runtime.h b/code/chapter-12/runtime.h new file mode 100644 index 0000000..aa297f2 --- /dev/null +++ b/code/chapter-12/runtime.h @@ -0,0 +1,1478 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_RUNTIME_H_ +#define ART_RUNTIME_RUNTIME_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "app_info.h" +#include "base/locks.h" +#include "base/macros.h" +#include "base/mem_map.h" +#include "base/metrics/metrics.h" +#include "base/string_view_cpp20.h" +#include "compat_framework.h" +#include "deoptimization_kind.h" +#include "dex/dex_file_types.h" +#include "experimental_flags.h" +#include "gc_root.h" +#include "instrumentation.h" +#include "jdwp_provider.h" +#include "jni/jni_id_manager.h" +#include "jni_id_type.h" +#include "metrics/reporter.h" +#include "obj_ptr.h" +#include "offsets.h" +#include "process_state.h" +#include "quick/quick_method_frame_info.h" +#include "reflective_value_visitor.h" +#include "runtime_stats.h" + +namespace art { + +namespace gc { +class AbstractSystemWeakHolder; +class Heap; +} // namespace gc + +namespace hiddenapi { +enum class EnforcementPolicy; +} // namespace hiddenapi + +namespace jit { +class Jit; +class JitCodeCache; +class JitOptions; +} // namespace jit + +namespace mirror { +class Array; +class ClassLoader; +class DexCache; +template class ObjectArray; +template class PrimitiveArray; +typedef PrimitiveArray ByteArray; +class String; +class Throwable; +} // namespace mirror +namespace ti { +class Agent; +class AgentSpec; +} // namespace ti +namespace verifier { +class MethodVerifier; +enum class VerifyMode : int8_t; +} // namespace verifier +class ArenaPool; +class ArtMethod; +enum class CalleeSaveType: uint32_t; +class ClassLinker; +class CompilerCallbacks; +class Dex2oatImageTest; +class DexFile; +enum class InstructionSet; +class InternTable; +class IsMarkedVisitor; +class JavaVMExt; +class LinearAlloc; +class MonitorList; +class MonitorPool; +class NullPointerHandler; +class OatFileAssistantTest; +class OatFileManager; +class Plugin; +struct RuntimeArgumentMap; +class RuntimeCallbacks; +class SignalCatcher; +class StackOverflowHandler; +class SuspensionHandler; +class ThreadList; +class ThreadPool; +class Trace; +struct TraceConfig; +class Transaction; + +typedef std::vector> RuntimeOptions; + +typedef struct{ + char packageName[128]; + char jniModuleName[128]; + char jniFuncName[128]; + bool isRegisterNativePrint; + bool isJNIMethodPrint; + bool jniEnable; + void* kbacktrace=nullptr; +}PackageItem; + +class Runtime { + public: + // Parse raw runtime options. + static bool ParseOptions(const RuntimeOptions& raw_options, + bool ignore_unrecognized, + RuntimeArgumentMap* runtime_options); + + // Creates and initializes a new runtime. + static bool Create(RuntimeArgumentMap&& runtime_options) + SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_); + + // Creates and initializes a new runtime. + static bool Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) + SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_); + + bool EnsurePluginLoaded(const char* plugin_name, std::string* error_msg); + bool EnsurePerfettoPlugin(std::string* error_msg); + + // IsAotCompiler for compilers that don't have a running runtime. Only dex2oat currently. + bool IsAotCompiler() const { + return !UseJitCompilation() && IsCompiler(); + } + + // IsCompiler is any runtime which has a running compiler, either dex2oat or JIT. + bool IsCompiler() const { + return compiler_callbacks_ != nullptr; + } + + // If a compiler, are we compiling a boot image? + bool IsCompilingBootImage() const; + + bool CanRelocate() const; + + bool ShouldRelocate() const { + return must_relocate_ && CanRelocate(); + } + + bool MustRelocateIfPossible() const { + return must_relocate_; + } + + bool IsImageDex2OatEnabled() const { + return image_dex2oat_enabled_; + } + + CompilerCallbacks* GetCompilerCallbacks() { + return compiler_callbacks_; + } + + void SetCompilerCallbacks(CompilerCallbacks* callbacks) { + CHECK(callbacks != nullptr); + compiler_callbacks_ = callbacks; + } + + bool IsZygote() const { + return is_zygote_; + } + + bool IsPrimaryZygote() const { + return is_primary_zygote_; + } + + bool IsSystemServer() const { + return is_system_server_; + } + + void SetAsSystemServer() { + is_system_server_ = true; + is_zygote_ = false; + is_primary_zygote_ = false; + } + + void SetAsZygoteChild(bool is_system_server, bool is_zygote) { + // System server should have been set earlier in SetAsSystemServer. + CHECK_EQ(is_system_server_, is_system_server); + is_zygote_ = is_zygote; + is_primary_zygote_ = false; + } + + bool IsExplicitGcDisabled() const { + return is_explicit_gc_disabled_; + } + + std::string GetCompilerExecutable() const; + + const std::vector& GetCompilerOptions() const { + return compiler_options_; + } + + void AddCompilerOption(const std::string& option) { + compiler_options_.push_back(option); + } + + const std::vector& GetImageCompilerOptions() const { + return image_compiler_options_; + } + + const std::string& GetImageLocation() const { + return image_location_; + } + + // Starts a runtime, which may cause threads to be started and code to run. + bool Start() UNLOCK_FUNCTION(Locks::mutator_lock_); + + bool IsShuttingDown(Thread* self); + bool IsShuttingDownLocked() const REQUIRES(Locks::runtime_shutdown_lock_) { + return shutting_down_; + } + + size_t NumberOfThreadsBeingBorn() const REQUIRES(Locks::runtime_shutdown_lock_) { + return threads_being_born_; + } + + void StartThreadBirth() REQUIRES(Locks::runtime_shutdown_lock_) { + threads_being_born_++; + } + + void EndThreadBirth() REQUIRES(Locks::runtime_shutdown_lock_); + + bool IsStarted() const { + return started_; + } + + bool IsFinishedStarting() const { + return finished_starting_; + } + + void RunRootClinits(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + + + + static Runtime* Current() { + return instance_; + } + + void SetConfigItem(PackageItem item){ + configItem=item; + } + + PackageItem& GetConfigItem(){ + return configItem; + } + + // Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most + // callers should prefer. + NO_RETURN static void Abort(const char* msg) REQUIRES(!Locks::abort_lock_); + + // Returns the "main" ThreadGroup, used when attaching user threads. + jobject GetMainThreadGroup() const; + + // Returns the "system" ThreadGroup, used when attaching our internal threads. + jobject GetSystemThreadGroup() const; + + // Returns the system ClassLoader which represents the CLASSPATH. + jobject GetSystemClassLoader() const; + + // Attaches the calling native thread to the runtime. + bool AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group, + bool create_peer); + + void CallExitHook(jint status); + + // Detaches the current native thread from the runtime. + void DetachCurrentThread() REQUIRES(!Locks::mutator_lock_); + + void DumpDeoptimizations(std::ostream& os); + void DumpForSigQuit(std::ostream& os); + void DumpLockHolders(std::ostream& os); + + ~Runtime(); + + const std::vector& GetBootClassPath() const { + return boot_class_path_; + } + + const std::vector& GetBootClassPathLocations() const { + DCHECK(boot_class_path_locations_.empty() || + boot_class_path_locations_.size() == boot_class_path_.size()); + return boot_class_path_locations_.empty() ? boot_class_path_ : boot_class_path_locations_; + } + + // Returns the checksums for the boot image, extensions and extra boot class path dex files, + // based on the image spaces and boot class path dex files loaded in memory. + const std::string& GetBootClassPathChecksums() const { + return boot_class_path_checksums_; + } + + const std::string& GetClassPathString() const { + return class_path_string_; + } + + ClassLinker* GetClassLinker() const { + return class_linker_; + } + + jni::JniIdManager* GetJniIdManager() const { + return jni_id_manager_.get(); + } + + size_t GetDefaultStackSize() const { + return default_stack_size_; + } + + unsigned int GetFinalizerTimeoutMs() const { + return finalizer_timeout_ms_; + } + + gc::Heap* GetHeap() const { + return heap_; + } + + InternTable* GetInternTable() const { + DCHECK(intern_table_ != nullptr); + return intern_table_; + } + + JavaVMExt* GetJavaVM() const { + return java_vm_.get(); + } + + size_t GetMaxSpinsBeforeThinLockInflation() const { + return max_spins_before_thin_lock_inflation_; + } + + MonitorList* GetMonitorList() const { + return monitor_list_; + } + + MonitorPool* GetMonitorPool() const { + return monitor_pool_; + } + + // Is the given object the special object used to mark a cleared JNI weak global? + bool IsClearedJniWeakGlobal(ObjPtr obj) REQUIRES_SHARED(Locks::mutator_lock_); + + // Get the special object used to mark a cleared JNI weak global. + mirror::Object* GetClearedJniWeakGlobal() REQUIRES_SHARED(Locks::mutator_lock_); + + mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenThrowingException() + REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME() + REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow() + REQUIRES_SHARED(Locks::mutator_lock_); + + mirror::Throwable* GetPreAllocatedNoClassDefFoundError() + REQUIRES_SHARED(Locks::mutator_lock_); + + const std::vector& GetProperties() const { + return properties_; + } + + ThreadList* GetThreadList() const { + return thread_list_; + } + + static const char* GetVersion() { + return "2.1.0"; + } + + bool IsMethodHandlesEnabled() const { + return true; + } + + void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); + void AllowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_); + // broadcast_for_checkpoint is true when we broadcast for making blocking threads to respond to + // checkpoint requests. It's false when we broadcast to unblock blocking threads after system weak + // access is reenabled. + void BroadcastForNewSystemWeaks(bool broadcast_for_checkpoint = false); + + // Visit all the roots. If only_dirty is true then non-dirty roots won't be visited. If + // clean_dirty is true then dirty roots will be marked as non-dirty after visiting. + void VisitRoots(RootVisitor* visitor, VisitRootFlags flags = kVisitRootFlagAllRoots) + REQUIRES(!Locks::classlinker_classes_lock_, !Locks::trace_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Visit image roots, only used for hprof since the GC uses the image space mod union table + // instead. + void VisitImageRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); + + // Visit all of the roots we can safely visit concurrently. + void VisitConcurrentRoots(RootVisitor* visitor, + VisitRootFlags flags = kVisitRootFlagAllRoots) + REQUIRES(!Locks::classlinker_classes_lock_, !Locks::trace_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Visit all of the non thread roots, we can do this with mutators unpaused. + void VisitNonThreadRoots(RootVisitor* visitor) + REQUIRES_SHARED(Locks::mutator_lock_); + + void VisitTransactionRoots(RootVisitor* visitor) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Sweep system weaks, the system weak is deleted if the visitor return null. Otherwise, the + // system weak is updated to be the visitor's returned value. + void SweepSystemWeaks(IsMarkedVisitor* visitor) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Walk all reflective objects and visit their targets as well as any method/fields held by the + // runtime threads that are marked as being reflective. + void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) REQUIRES(Locks::mutator_lock_); + // Helper for visiting reflective targets with lambdas for both field and method reflective + // targets. + template + void VisitReflectiveTargets(FieldVis&& fv, MethodVis&& mv) REQUIRES(Locks::mutator_lock_) { + FunctionReflectiveValueVisitor frvv(fv, mv); + VisitReflectiveTargets(&frvv); + } + + // Returns a special method that calls into a trampoline for runtime method resolution + ArtMethod* GetResolutionMethod(); + + bool HasResolutionMethod() const { + return resolution_method_ != nullptr; + } + + void SetResolutionMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + void ClearResolutionMethod() { + resolution_method_ = nullptr; + } + + ArtMethod* CreateResolutionMethod() REQUIRES_SHARED(Locks::mutator_lock_); + + // Returns a special method that calls into a trampoline for runtime imt conflicts. + ArtMethod* GetImtConflictMethod(); + ArtMethod* GetImtUnimplementedMethod(); + + bool HasImtConflictMethod() const { + return imt_conflict_method_ != nullptr; + } + + void ClearImtConflictMethod() { + imt_conflict_method_ = nullptr; + } + + void FixupConflictTables() REQUIRES_SHARED(Locks::mutator_lock_); + void SetImtConflictMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + void SetImtUnimplementedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + + ArtMethod* CreateImtConflictMethod(LinearAlloc* linear_alloc) + REQUIRES_SHARED(Locks::mutator_lock_); + + void ClearImtUnimplementedMethod() { + imt_unimplemented_method_ = nullptr; + } + + bool HasCalleeSaveMethod(CalleeSaveType type) const { + return callee_save_methods_[static_cast(type)] != 0u; + } + + ArtMethod* GetCalleeSaveMethod(CalleeSaveType type) + REQUIRES_SHARED(Locks::mutator_lock_); + + ArtMethod* GetCalleeSaveMethodUnchecked(CalleeSaveType type) + REQUIRES_SHARED(Locks::mutator_lock_); + + QuickMethodFrameInfo GetRuntimeMethodFrameInfo(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_); + + static constexpr size_t GetCalleeSaveMethodOffset(CalleeSaveType type) { + return OFFSETOF_MEMBER(Runtime, callee_save_methods_[static_cast(type)]); + } + + InstructionSet GetInstructionSet() const { + return instruction_set_; + } + + void SetInstructionSet(InstructionSet instruction_set); + void ClearInstructionSet(); + + void SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type); + void ClearCalleeSaveMethods(); + + ArtMethod* CreateCalleeSaveMethod() REQUIRES_SHARED(Locks::mutator_lock_); + + uint64_t GetStat(int kind); + + RuntimeStats* GetStats() { + return &stats_; + } + + bool HasStatsEnabled() const { + return stats_enabled_; + } + + void ResetStats(int kinds); + + void SetStatsEnabled(bool new_state) + REQUIRES(!Locks::instrument_entrypoints_lock_, !Locks::mutator_lock_); + + enum class NativeBridgeAction { // private + kUnload, + kInitialize + }; + + jit::Jit* GetJit() const { + return jit_.get(); + } + + jit::JitCodeCache* GetJitCodeCache() const { + return jit_code_cache_.get(); + } + + // Returns true if JIT compilations are enabled. GetJit() will be not null in this case. + bool UseJitCompilation() const; + + void PreZygoteFork(); + void PostZygoteFork(); + void InitNonZygoteOrPostFork( + JNIEnv* env, + bool is_system_server, + bool is_child_zygote, + NativeBridgeAction action, + const char* isa, + bool profile_system_server = false); + + const instrumentation::Instrumentation* GetInstrumentation() const { + return &instrumentation_; + } + + instrumentation::Instrumentation* GetInstrumentation() { + return &instrumentation_; + } + + void RegisterAppInfo(const std::string& package_name, + const std::vector& code_paths, + const std::string& profile_output_filename, + const std::string& ref_profile_filename, + int32_t code_type); + + // Transaction support. + bool IsActiveTransaction() const; + void EnterTransactionMode(bool strict, mirror::Class* root); + void ExitTransactionMode(); + void RollbackAllTransactions() REQUIRES_SHARED(Locks::mutator_lock_); + // Transaction rollback and exit transaction are always done together, it's convenience to + // do them in one function. + void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_); + bool IsTransactionAborted() const; + const std::unique_ptr& GetTransaction() const; + bool IsActiveStrictTransactionMode() const; + + void AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) + REQUIRES_SHARED(Locks::mutator_lock_); + void ThrowTransactionAbortError(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_); + + void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value, + bool is_volatile) const; + void RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset, int8_t value, + bool is_volatile) const; + void RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset, uint16_t value, + bool is_volatile) const; + void RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset, int16_t value, + bool is_volatile) const; + void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value, + bool is_volatile) const; + void RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value, + bool is_volatile) const; + void RecordWriteFieldReference(mirror::Object* obj, + MemberOffset field_offset, + ObjPtr value, + bool is_volatile) const + REQUIRES_SHARED(Locks::mutator_lock_); + void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const + REQUIRES_SHARED(Locks::mutator_lock_); + void RecordStrongStringInsertion(ObjPtr s) const + REQUIRES(Locks::intern_table_lock_); + void RecordWeakStringInsertion(ObjPtr s) const + REQUIRES(Locks::intern_table_lock_); + void RecordStrongStringRemoval(ObjPtr s) const + REQUIRES(Locks::intern_table_lock_); + void RecordWeakStringRemoval(ObjPtr s) const + REQUIRES(Locks::intern_table_lock_); + void RecordResolveString(ObjPtr dex_cache, dex::StringIndex string_idx) const + REQUIRES_SHARED(Locks::mutator_lock_); + + void SetFaultMessage(const std::string& message); + + void AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector* arg_vector) const; + + bool ExplicitStackOverflowChecks() const { + return !implicit_so_checks_; + } + + void DisableVerifier(); + bool IsVerificationEnabled() const; + bool IsVerificationSoftFail() const; + + void SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) { + hidden_api_policy_ = policy; + } + + hiddenapi::EnforcementPolicy GetHiddenApiEnforcementPolicy() const { + return hidden_api_policy_; + } + + void SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) { + core_platform_api_policy_ = policy; + } + + hiddenapi::EnforcementPolicy GetCorePlatformApiEnforcementPolicy() const { + return core_platform_api_policy_; + } + + void SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) { + test_api_policy_ = policy; + } + + hiddenapi::EnforcementPolicy GetTestApiEnforcementPolicy() const { + return test_api_policy_; + } + + void SetHiddenApiExemptions(const std::vector& exemptions) { + hidden_api_exemptions_ = exemptions; + } + + const std::vector& GetHiddenApiExemptions() { + return hidden_api_exemptions_; + } + + void SetDedupeHiddenApiWarnings(bool value) { + dedupe_hidden_api_warnings_ = value; + } + + bool ShouldDedupeHiddenApiWarnings() { + return dedupe_hidden_api_warnings_; + } + + void SetHiddenApiEventLogSampleRate(uint32_t rate) { + hidden_api_access_event_log_rate_ = rate; + } + + uint32_t GetHiddenApiEventLogSampleRate() const { + return hidden_api_access_event_log_rate_; + } + + const std::string& GetProcessPackageName() const { + return process_package_name_; + } + + void SetProcessPackageName(const char* package_name) { + if (package_name == nullptr) { + process_package_name_.clear(); + } else { + process_package_name_ = package_name; + } + } + + const std::string& GetProcessDataDirectory() const { + return process_data_directory_; + } + + void SetProcessDataDirectory(const char* data_dir) { + if (data_dir == nullptr) { + process_data_directory_.clear(); + } else { + process_data_directory_ = data_dir; + } + } + + const std::vector& GetCpuAbilist() const { + return cpu_abilist_; + } + + bool IsRunningOnMemoryTool() const { + return is_running_on_memory_tool_; + } + + void SetTargetSdkVersion(uint32_t version) { + target_sdk_version_ = version; + } + + uint32_t GetTargetSdkVersion() const { + return target_sdk_version_; + } + + CompatFramework& GetCompatFramework() { + return compat_framework_; + } + + uint32_t GetZygoteMaxFailedBoots() const { + return zygote_max_failed_boots_; + } + + bool AreExperimentalFlagsEnabled(ExperimentalFlags flags) { + return (experimental_flags_ & flags) != ExperimentalFlags::kNone; + } + + void CreateJitCodeCache(bool rwx_memory_allowed); + + // Create the JIT and instrumentation and code cache. + void CreateJit(); + + ArenaPool* GetArenaPool() { + return arena_pool_.get(); + } + ArenaPool* GetJitArenaPool() { + return jit_arena_pool_.get(); + } + const ArenaPool* GetArenaPool() const { + return arena_pool_.get(); + } + + void ReclaimArenaPoolMemory(); + + LinearAlloc* GetLinearAlloc() { + return linear_alloc_.get(); + } + + jit::JitOptions* GetJITOptions() { + return jit_options_.get(); + } + + bool IsJavaDebuggable() const { + return is_java_debuggable_; + } + + void SetProfileableFromShell(bool value) { + is_profileable_from_shell_ = value; + } + + bool IsProfileableFromShell() const { + return is_profileable_from_shell_; + } + + void SetProfileable(bool value) { + is_profileable_ = value; + } + + bool IsProfileable() const { + return is_profileable_; + } + + void SetJavaDebuggable(bool value); + + // Deoptimize the boot image, called for Java debuggable apps. + void DeoptimizeBootImage() REQUIRES(Locks::mutator_lock_); + + bool IsNativeDebuggable() const { + return is_native_debuggable_; + } + + void SetNativeDebuggable(bool value) { + is_native_debuggable_ = value; + } + + void SetSignalHookDebuggable(bool value); + + bool AreNonStandardExitsEnabled() const { + return non_standard_exits_enabled_; + } + + void SetNonStandardExitsEnabled() { + DoAndMaybeSwitchInterpreter([=](){ non_standard_exits_enabled_ = true; }); + } + + bool AreAsyncExceptionsThrown() const { + return async_exceptions_thrown_; + } + + void SetAsyncExceptionsThrown() { + DoAndMaybeSwitchInterpreter([=](){ async_exceptions_thrown_ = true; }); + } + + // Change state and re-check which interpreter should be used. + // + // This must be called whenever there is an event that forces + // us to use different interpreter (e.g. debugger is attached). + // + // Changing the state using the lamda gives us some multihreading safety. + // It ensures that two calls do not interfere with each other and + // it makes it possible to DCHECK that thread local flag is correct. + template + static void DoAndMaybeSwitchInterpreter(Action lamda); + + // Returns the build fingerprint, if set. Otherwise an empty string is returned. + std::string GetFingerprint() { + return fingerprint_; + } + + // Called from class linker. + void SetSentinel(ObjPtr sentinel) REQUIRES_SHARED(Locks::mutator_lock_); + // For testing purpose only. + // TODO: Remove this when this is no longer needed (b/116087961). + GcRoot GetSentinel() REQUIRES_SHARED(Locks::mutator_lock_); + + + // Use a sentinel for marking entries in a table that have been cleared. + // This helps diagnosing in case code tries to wrongly access such + // entries. + static mirror::Class* GetWeakClassSentinel() { + return reinterpret_cast(0xebadbeef); + } + + // Helper for the GC to process a weak class in a table. + static void ProcessWeakClass(GcRoot* root_ptr, + IsMarkedVisitor* visitor, + mirror::Class* update) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Create a normal LinearAlloc or low 4gb version if we are 64 bit AOT compiler. + LinearAlloc* CreateLinearAlloc(); + + OatFileManager& GetOatFileManager() const { + DCHECK(oat_file_manager_ != nullptr); + return *oat_file_manager_; + } + + double GetHashTableMinLoadFactor() const; + double GetHashTableMaxLoadFactor() const; + + bool IsSafeMode() const { + return safe_mode_; + } + + void SetSafeMode(bool mode) { + safe_mode_ = mode; + } + + bool GetDumpNativeStackOnSigQuit() const { + return dump_native_stack_on_sig_quit_; + } + + void UpdateProcessState(ProcessState process_state); + + // Returns true if we currently care about long mutator pause. + bool InJankPerceptibleProcessState() const { + return process_state_ == kProcessStateJankPerceptible; + } + + void RegisterSensitiveThread() const; + + void SetZygoteNoThreadSection(bool val) { + zygote_no_threads_ = val; + } + + bool IsZygoteNoThreadSection() const { + return zygote_no_threads_; + } + + // Returns if the code can be deoptimized asynchronously. Code may be compiled with some + // optimization that makes it impossible to deoptimize. + bool IsAsyncDeoptimizeable(uintptr_t code) const REQUIRES_SHARED(Locks::mutator_lock_); + + // Returns a saved copy of the environment (getenv/setenv values). + // Used by Fork to protect against overwriting LD_LIBRARY_PATH, etc. + char** GetEnvSnapshot() const { + return env_snapshot_.GetSnapshot(); + } + + void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); + void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); + + void AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader); + + const std::list>& GetAgents() const { + return agents_; + } + + RuntimeCallbacks* GetRuntimeCallbacks(); + + bool HasLoadedPlugins() const { + return !plugins_.empty(); + } + + void InitThreadGroups(Thread* self); + + void SetDumpGCPerformanceOnShutdown(bool value) { + dump_gc_performance_on_shutdown_ = value; + } + + bool GetDumpGCPerformanceOnShutdown() const { + return dump_gc_performance_on_shutdown_; + } + + void IncrementDeoptimizationCount(DeoptimizationKind kind) { + DCHECK_LE(kind, DeoptimizationKind::kLast); + deoptimization_counts_[static_cast(kind)]++; + } + + uint32_t GetNumberOfDeoptimizations() const { + uint32_t result = 0; + for (size_t i = 0; i <= static_cast(DeoptimizationKind::kLast); ++i) { + result += deoptimization_counts_[i]; + } + return result; + } + + bool DenyArtApexDataFiles() const { + return deny_art_apex_data_files_; + } + + // Whether or not we use MADV_RANDOM on files that are thought to have random access patterns. + // This is beneficial for low RAM devices since it reduces page cache thrashing. + bool MAdviseRandomAccess() const { + return madvise_random_access_; + } + + size_t GetMadviseWillNeedSizeVdex() const { + return madvise_willneed_vdex_filesize_; + } + + size_t GetMadviseWillNeedSizeOdex() const { + return madvise_willneed_odex_filesize_; + } + + size_t GetMadviseWillNeedSizeArt() const { + return madvise_willneed_art_filesize_; + } + + const std::string& GetJdwpOptions() { + return jdwp_options_; + } + + JdwpProvider GetJdwpProvider() const { + return jdwp_provider_; + } + + JniIdType GetJniIdType() const { + return jni_ids_indirection_; + } + + bool CanSetJniIdType() const { + return GetJniIdType() == JniIdType::kSwapablePointer; + } + + // Changes the JniIdType to the given type. Only allowed if CanSetJniIdType(). All threads must be + // suspended to call this function. + void SetJniIdType(JniIdType t); + + uint32_t GetVerifierLoggingThresholdMs() const { + return verifier_logging_threshold_ms_; + } + + // Atomically delete the thread pool if the reference count is 0. + bool DeleteThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_); + + // Wait for all the thread workers to be attached. + void WaitForThreadPoolWorkersToStart() REQUIRES(!Locks::runtime_thread_pool_lock_); + + // Scoped usage of the runtime thread pool. Prevents the pool from being + // deleted. Note that the thread pool is only for startup and gets deleted after. + class ScopedThreadPoolUsage { + public: + ScopedThreadPoolUsage(); + ~ScopedThreadPoolUsage(); + + // Return the thread pool. + ThreadPool* GetThreadPool() const { + return thread_pool_; + } + + private: + ThreadPool* const thread_pool_; + }; + + bool LoadAppImageStartupCache() const { + return load_app_image_startup_cache_; + } + + void SetLoadAppImageStartupCacheEnabled(bool enabled) { + load_app_image_startup_cache_ = enabled; + } + + // Reset the startup completed status so that we can call NotifyStartupCompleted again. Should + // only be used for testing. + void ResetStartupCompleted(); + + // Notify the runtime that application startup is considered completed. Only has effect for the + // first call. + void NotifyStartupCompleted(); + + // Notify the runtime that the application finished loading some dex/odex files. This is + // called everytime we load a set of dex files in a class loader. + void NotifyDexFileLoaded(); + + // Return true if startup is already completed. + bool GetStartupCompleted() const; + + bool IsVerifierMissingKThrowFatal() const { + return verifier_missing_kthrow_fatal_; + } + + bool IsJavaZygoteForkLoopRequired() const { + return force_java_zygote_fork_loop_; + } + + bool IsPerfettoHprofEnabled() const { + return perfetto_hprof_enabled_; + } + + bool IsPerfettoJavaHeapStackProfEnabled() const { + return perfetto_javaheapprof_enabled_; + } + + bool IsMonitorTimeoutEnabled() const { + return monitor_timeout_enable_; + } + + uint64_t GetMonitorTimeoutNs() const { + return monitor_timeout_ns_; + } + // Return true if we should load oat files as executable or not. + bool GetOatFilesExecutable() const; + + metrics::ArtMetrics* GetMetrics() { return &metrics_; } + + AppInfo* GetAppInfo() { return &app_info_; } + + void RequestMetricsReport(bool synchronous = true); + + static void MadviseFileForRange(size_t madvise_size_limit_bytes, + size_t map_size_bytes, + const uint8_t* map_begin, + const uint8_t* map_end, + const std::string& file_name); + + const std::string& GetApexVersions() const { + return apex_versions_; + } + + // Trigger a flag reload from system properties or device congfigs. + // + // Should only be called from runtime init and zygote post fork as + // we don't want to change the runtime config midway during execution. + // + // The caller argument should be the name of the function making this call + // and will be used to enforce the appropriate names. + // + // See Flags::ReloadAllFlags as well. + static void ReloadAllFlags(const std::string& caller); + + private: + static void InitPlatformSignalHandlers(); + + Runtime(); + + void BlockSignals(); + + bool Init(RuntimeArgumentMap&& runtime_options) + SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_); + void InitNativeMethods() REQUIRES(!Locks::mutator_lock_); + void RegisterRuntimeNativeMethods(JNIEnv* env); + void InitMetrics(); + + void StartDaemonThreads(); + void StartSignalCatcher(); + + void MaybeSaveJitProfilingInfo(); + + // Visit all of the thread roots. + void VisitThreadRoots(RootVisitor* visitor, VisitRootFlags flags) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Visit all other roots which must be done with mutators suspended. + void VisitNonConcurrentRoots(RootVisitor* visitor, VisitRootFlags flags) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Constant roots are the roots which never change after the runtime is initialized, they only + // need to be visited once per GC cycle. + void VisitConstantRoots(RootVisitor* visitor) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Note: To be lock-free, GetFaultMessage temporarily replaces the lock message with null. + // As such, there is a window where a call will return an empty string. In general, + // only aborting code should retrieve this data (via GetFaultMessageForAbortLogging + // friend). + std::string GetFaultMessage(); + + ThreadPool* AcquireThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_); + void ReleaseThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_); + + // Parses /apex/apex-info-list.xml to initialize a string containing versions + // of boot classpath jars and encoded into .oat files. + void InitializeApexVersions(); + + // A pointer to the active runtime or null. + static Runtime* instance_; + + // NOTE: these must match the gc::ProcessState values as they come directly from the framework. + static constexpr int kProfileForground = 0; + static constexpr int kProfileBackground = 1; + + static constexpr uint32_t kCalleeSaveSize = 6u; + + PackageItem configItem; + + typedef const char* (*kbacktraceFunc)(bool with_context,const char* moduleName); + + kbacktraceFunc kbacktrace=nullptr; + + // 64 bit so that we can share the same asm offsets for both 32 and 64 bits. + uint64_t callee_save_methods_[kCalleeSaveSize]; + // Pre-allocated exceptions (see Runtime::Init). + GcRoot pre_allocated_OutOfMemoryError_when_throwing_exception_; + GcRoot pre_allocated_OutOfMemoryError_when_throwing_oome_; + GcRoot pre_allocated_OutOfMemoryError_when_handling_stack_overflow_; + GcRoot pre_allocated_NoClassDefFoundError_; + ArtMethod* resolution_method_; + ArtMethod* imt_conflict_method_; + // Unresolved method has the same behavior as the conflict method, it is used by the class linker + // for differentiating between unfilled imt slots vs conflict slots in superclasses. + ArtMethod* imt_unimplemented_method_; + + // Special sentinel object used to invalid conditions in JNI (cleared weak references) and + // JDWP (invalid references). + GcRoot sentinel_; + + InstructionSet instruction_set_; + + CompilerCallbacks* compiler_callbacks_; + bool is_zygote_; + bool is_primary_zygote_; + bool is_system_server_; + bool must_relocate_; + bool is_concurrent_gc_enabled_; + bool is_explicit_gc_disabled_; + bool image_dex2oat_enabled_; + + std::string compiler_executable_; + std::vector compiler_options_; + std::vector image_compiler_options_; + std::string image_location_; + + std::vector boot_class_path_; + std::vector boot_class_path_locations_; + std::string boot_class_path_checksums_; + std::string class_path_string_; + std::vector properties_; + + std::list agent_specs_; + std::list> agents_; + std::vector plugins_; + + // The default stack size for managed threads created by the runtime. + size_t default_stack_size_; + + // Finalizers running for longer than this many milliseconds abort the runtime. + unsigned int finalizer_timeout_ms_; + + gc::Heap* heap_; + + std::unique_ptr jit_arena_pool_; + std::unique_ptr arena_pool_; + // Special low 4gb pool for compiler linear alloc. We need ArtFields to be in low 4gb if we are + // compiling using a 32 bit image on a 64 bit compiler in case we resolve things in the image + // since the field arrays are int arrays in this case. + std::unique_ptr low_4gb_arena_pool_; + + // Shared linear alloc for now. + std::unique_ptr linear_alloc_; + + // The number of spins that are done before thread suspension is used to forcibly inflate. + size_t max_spins_before_thin_lock_inflation_; + MonitorList* monitor_list_; + MonitorPool* monitor_pool_; + + ThreadList* thread_list_; + + InternTable* intern_table_; + + ClassLinker* class_linker_; + + SignalCatcher* signal_catcher_; + + std::unique_ptr jni_id_manager_; + + std::unique_ptr java_vm_; + + std::unique_ptr jit_; + std::unique_ptr jit_code_cache_; + std::unique_ptr jit_options_; + + // Runtime thread pool. The pool is only for startup and gets deleted after. + std::unique_ptr thread_pool_ GUARDED_BY(Locks::runtime_thread_pool_lock_); + size_t thread_pool_ref_count_ GUARDED_BY(Locks::runtime_thread_pool_lock_); + + // Fault message, printed when we get a SIGSEGV. Stored as a native-heap object and accessed + // lock-free, so needs to be atomic. + std::atomic fault_message_; + + // A non-zero value indicates that a thread has been created but not yet initialized. Guarded by + // the shutdown lock so that threads aren't born while we're shutting down. + size_t threads_being_born_ GUARDED_BY(Locks::runtime_shutdown_lock_); + + // Waited upon until no threads are being born. + std::unique_ptr shutdown_cond_ GUARDED_BY(Locks::runtime_shutdown_lock_); + + // Set when runtime shutdown is past the point that new threads may attach. + bool shutting_down_ GUARDED_BY(Locks::runtime_shutdown_lock_); + + // The runtime is starting to shutdown but is blocked waiting on shutdown_cond_. + bool shutting_down_started_ GUARDED_BY(Locks::runtime_shutdown_lock_); + + bool started_; + + // New flag added which tells us if the runtime has finished starting. If + // this flag is set then the Daemon threads are created and the class loader + // is created. This flag is needed for knowing if its safe to request CMS. + bool finished_starting_; + + // Hooks supported by JNI_CreateJavaVM + jint (*vfprintf_)(FILE* stream, const char* format, va_list ap); + void (*exit_)(jint status); + void (*abort_)(); + + bool stats_enabled_; + RuntimeStats stats_; + + const bool is_running_on_memory_tool_; + + std::unique_ptr trace_config_; + + instrumentation::Instrumentation instrumentation_; + + jobject main_thread_group_; + jobject system_thread_group_; + + // As returned by ClassLoader.getSystemClassLoader(). + jobject system_class_loader_; + + // If true, then we dump the GC cumulative timings on shutdown. + bool dump_gc_performance_on_shutdown_; + + // Transactions used for pre-initializing classes at compilation time. + // Support nested transactions, maintain a list containing all transactions. Transactions are + // handled under a stack discipline. Because GC needs to go over all transactions, we choose list + // as substantial data structure instead of stack. + std::list> preinitialization_transactions_; + + // If kNone, verification is disabled. kEnable by default. + verifier::VerifyMode verify_; + + // List of supported cpu abis. + std::vector cpu_abilist_; + + // Specifies target SDK version to allow workarounds for certain API levels. + uint32_t target_sdk_version_; + + // ART counterpart for the compat framework (go/compat-framework). + CompatFramework compat_framework_; + + // Implicit checks flags. + bool implicit_null_checks_; // NullPointer checks are implicit. + bool implicit_so_checks_; // StackOverflow checks are implicit. + bool implicit_suspend_checks_; // Thread suspension checks are implicit. + + // Whether or not the sig chain (and implicitly the fault handler) should be + // disabled. Tools like dex2oat don't need them. This enables + // building a statically link version of dex2oat. + bool no_sig_chain_; + + // Force the use of native bridge even if the app ISA matches the runtime ISA. + bool force_native_bridge_; + + // Whether or not a native bridge has been loaded. + // + // The native bridge allows running native code compiled for a foreign ISA. The way it works is, + // if standard dlopen fails to load native library associated with native activity, it calls to + // the native bridge to load it and then gets the trampoline for the entry to native activity. + // + // The option 'native_bridge_library_filename' specifies the name of the native bridge. + // When non-empty the native bridge will be loaded from the given file. An empty value means + // that there's no native bridge. + bool is_native_bridge_loaded_; + + // Whether we are running under native debugger. + bool is_native_debuggable_; + + // whether or not any async exceptions have ever been thrown. This is used to speed up the + // MterpShouldSwitchInterpreters function. + bool async_exceptions_thrown_; + + // Whether anything is going to be using the shadow-frame APIs to force a function to return + // early. Doing this requires that (1) we be debuggable and (2) that mterp is exited. + bool non_standard_exits_enabled_; + + // Whether Java code needs to be debuggable. + bool is_java_debuggable_; + + bool monitor_timeout_enable_; + uint64_t monitor_timeout_ns_; + + // Whether or not this application can be profiled by the shell user, + // even when running on a device that is running in user mode. + bool is_profileable_from_shell_ = false; + + // Whether or not this application can be profiled by system services on a + // device running in user mode, but not necessarily by the shell user. + bool is_profileable_ = false; + + // The maximum number of failed boots we allow before pruning the dalvik cache + // and trying again. This option is only inspected when we're running as a + // zygote. + uint32_t zygote_max_failed_boots_; + + // Enable experimental opcodes that aren't fully specified yet. The intent is to + // eventually publish them as public-usable opcodes, but they aren't ready yet. + // + // Experimental opcodes should not be used by other production code. + ExperimentalFlags experimental_flags_; + + // Contains the build fingerprint, if given as a parameter. + std::string fingerprint_; + + // Oat file manager, keeps track of what oat files are open. + OatFileManager* oat_file_manager_; + + // Whether or not we are on a low RAM device. + bool is_low_memory_mode_; + + // Whether or not we use MADV_RANDOM on files that are thought to have random access patterns. + // This is beneficial for low RAM devices since it reduces page cache thrashing. + bool madvise_random_access_; + + // Limiting size (in bytes) for applying MADV_WILLNEED on vdex files + // A 0 for this will turn off madvising to MADV_WILLNEED + size_t madvise_willneed_vdex_filesize_; + + // Limiting size (in bytes) for applying MADV_WILLNEED on odex files + // A 0 for this will turn off madvising to MADV_WILLNEED + size_t madvise_willneed_odex_filesize_; + + // Limiting size (in bytes) for applying MADV_WILLNEED on art files + // A 0 for this will turn off madvising to MADV_WILLNEED + size_t madvise_willneed_art_filesize_; + + // Whether the application should run in safe mode, that is, interpreter only. + bool safe_mode_; + + // Whether access checks on hidden API should be performed. + hiddenapi::EnforcementPolicy hidden_api_policy_; + + // Whether access checks on core platform API should be performed. + hiddenapi::EnforcementPolicy core_platform_api_policy_; + + // Whether access checks on test API should be performed. + hiddenapi::EnforcementPolicy test_api_policy_; + + // List of signature prefixes of methods that have been removed from the blacklist, and treated + // as if whitelisted. + std::vector hidden_api_exemptions_; + + // Do not warn about the same hidden API access violation twice. + // This is only used for testing. + bool dedupe_hidden_api_warnings_; + + // How often to log hidden API access to the event log. An integer between 0 + // (never) and 0x10000 (always). + uint32_t hidden_api_access_event_log_rate_; + + // The package of the app running in this process. + std::string process_package_name_; + + // The data directory of the app running in this process. + std::string process_data_directory_; + + // Whether threads should dump their native stack on SIGQUIT. + bool dump_native_stack_on_sig_quit_; + + // Whether or not we currently care about pause times. + ProcessState process_state_; + + // Whether zygote code is in a section that should not start threads. + bool zygote_no_threads_; + + // The string containing requested jdwp options + std::string jdwp_options_; + + // The jdwp provider we were configured with. + JdwpProvider jdwp_provider_; + + // True if jmethodID and jfieldID are opaque Indices. When false (the default) these are simply + // pointers. This is set by -Xopaque-jni-ids:{true,false}. + JniIdType jni_ids_indirection_; + + // Set to false in cases where we want to directly control when jni-id + // indirection is changed. This is intended only for testing JNI id swapping. + bool automatically_set_jni_ids_indirection_; + + // True if files in /data/misc/apexdata/com.android.art are considered untrustworthy. + bool deny_art_apex_data_files_; + + // Saved environment. + class EnvSnapshot { + public: + EnvSnapshot() = default; + void TakeSnapshot(); + char** GetSnapshot() const; + + private: + std::unique_ptr c_env_vector_; + std::vector> name_value_pairs_; + + DISALLOW_COPY_AND_ASSIGN(EnvSnapshot); + } env_snapshot_; + + // Generic system-weak holders. + std::vector system_weak_holders_; + + std::unique_ptr callbacks_; + + std::atomic deoptimization_counts_[ + static_cast(DeoptimizationKind::kLast) + 1]; + + MemMap protected_fault_page_; + + uint32_t verifier_logging_threshold_ms_; + + bool load_app_image_startup_cache_ = false; + + // If startup has completed, must happen at most once. + std::atomic startup_completed_ = false; + + bool verifier_missing_kthrow_fatal_; + bool force_java_zygote_fork_loop_; + bool perfetto_hprof_enabled_; + bool perfetto_javaheapprof_enabled_; + + metrics::ArtMetrics metrics_; + std::unique_ptr metrics_reporter_; + + // Apex versions of boot classpath jars concatenated in a string. The format + // is of the type: + // '/apex1_version/apex2_version//' + // + // When the apex is the factory version, we don't encode it (for example in + // the third entry in the example above). + std::string apex_versions_; + + // The info about the application code paths. + AppInfo app_info_; + + // Note: See comments on GetFaultMessage. + friend std::string GetFaultMessageForAbortLogging(); + friend class Dex2oatImageTest; + friend class ScopedThreadPoolUsage; + friend class OatFileAssistantTest; + class NotifyStartupCompletedTask; + + DISALLOW_COPY_AND_ASSIGN(Runtime); +}; + +inline metrics::ArtMetrics* GetMetrics() { return Runtime::Current()->GetMetrics(); } + +} // namespace art + +#endif // ART_RUNTIME_RUNTIME_H_ diff --git a/code/chapter-12/xUnwind.zip b/code/chapter-12/xUnwind.zip new file mode 100644 index 0000000..475638b Binary files /dev/null and b/code/chapter-12/xUnwind.zip differ