Flutter Engine
The Flutter Engine
ApplicationInfoLoader.java
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package io.flutter.embedding.engine.loader;
6
7import android.content.Context;
8import android.content.pm.ApplicationInfo;
9import android.content.pm.PackageManager;
10import android.content.res.XmlResourceParser;
11import android.os.Bundle;
12import androidx.annotation.NonNull;
13import java.io.IOException;
14import org.json.JSONArray;
15import org.xmlpull.v1.XmlPullParserException;
16
17/** Loads application information given a Context. */
18public final class ApplicationInfoLoader {
19 // XML Attribute keys supported in AndroidManifest.xml
20 public static final String PUBLIC_AOT_SHARED_LIBRARY_NAME =
22 public static final String PUBLIC_VM_SNAPSHOT_DATA_KEY =
23 FlutterLoader.class.getName() + '.' + FlutterLoader.VM_SNAPSHOT_DATA_KEY;
24 public static final String PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY =
26 public static final String PUBLIC_FLUTTER_ASSETS_DIR_KEY =
28 public static final String NETWORK_POLICY_METADATA_KEY = "io.flutter.network-policy";
31
32 @NonNull
33 private static ApplicationInfo getApplicationInfo(@NonNull Context applicationContext) {
34 try {
35 return applicationContext
36 .getPackageManager()
37 .getApplicationInfo(applicationContext.getPackageName(), PackageManager.GET_META_DATA);
38 } catch (PackageManager.NameNotFoundException e) {
39 throw new RuntimeException(e);
40 }
41 }
42
43 private static String getString(Bundle metadata, String key) {
44 if (metadata == null) {
45 return null;
46 }
47 return metadata.getString(key, null);
48 }
49
50 private static boolean getBoolean(Bundle metadata, String key, boolean defaultValue) {
51 if (metadata == null) {
52 return defaultValue;
53 }
54 return metadata.getBoolean(key, defaultValue);
55 }
56
57 private static String getNetworkPolicy(ApplicationInfo appInfo, Context context) {
58 // We cannot use reflection to look at networkSecurityConfigRes because
59 // Android throws an error when we try to access fields marked as This member is not intended
60 // for public use, and is only visible for testing..
61 // Instead we rely on metadata.
62 Bundle metadata = appInfo.metaData;
63 if (metadata == null) {
64 return null;
65 }
66
67 int networkSecurityConfigRes = metadata.getInt(NETWORK_POLICY_METADATA_KEY, 0);
68 if (networkSecurityConfigRes <= 0) {
69 return null;
70 }
71
72 JSONArray output = new JSONArray();
73 try {
74 XmlResourceParser xrp = context.getResources().getXml(networkSecurityConfigRes);
75 xrp.next();
76 int eventType = xrp.getEventType();
77 while (eventType != XmlResourceParser.END_DOCUMENT) {
78 if (eventType == XmlResourceParser.START_TAG) {
79 if (xrp.getName().equals("domain-config")) {
80 parseDomainConfig(xrp, output, false);
81 }
82 }
83 eventType = xrp.next();
84 }
85 } catch (IOException | XmlPullParserException e) {
86 return null;
87 }
88 return output.toString();
89 }
90
91 private static void parseDomainConfig(
92 XmlResourceParser xrp, JSONArray output, boolean inheritedCleartextPermitted)
93 throws IOException, XmlPullParserException {
94 boolean cleartextTrafficPermitted =
95 xrp.getAttributeBooleanValue(
96 null, "cleartextTrafficPermitted", inheritedCleartextPermitted);
97 while (true) {
98 int eventType = xrp.next();
99 if (eventType == XmlResourceParser.START_TAG) {
100 if (xrp.getName().equals("domain")) {
101 // There can be multiple domains.
102 parseDomain(xrp, output, cleartextTrafficPermitted);
103 } else if (xrp.getName().equals("domain-config")) {
104 parseDomainConfig(xrp, output, cleartextTrafficPermitted);
105 } else {
106 skipTag(xrp);
107 }
108 } else if (eventType == XmlResourceParser.END_TAG) {
109 break;
110 }
111 }
112 }
113
114 private static void skipTag(XmlResourceParser xrp) throws IOException, XmlPullParserException {
115 String name = xrp.getName();
116 int eventType = xrp.getEventType();
117 while (eventType != XmlResourceParser.END_TAG || xrp.getName() != name) {
118 eventType = xrp.next();
119 }
120 }
121
122 private static void parseDomain(
123 XmlResourceParser xrp, JSONArray output, boolean cleartextPermitted)
124 throws IOException, XmlPullParserException {
125 boolean includeSubDomains = xrp.getAttributeBooleanValue(null, "includeSubdomains", false);
126 xrp.next();
127 if (xrp.getEventType() != XmlResourceParser.TEXT) {
128 throw new IllegalStateException("Expected text");
129 }
130 String domain = xrp.getText().trim();
131 JSONArray outputArray = new JSONArray();
132 outputArray.put(domain);
133 outputArray.put(includeSubDomains);
134 outputArray.put(cleartextPermitted);
135 output.put(outputArray);
136 xrp.next();
137 if (xrp.getEventType() != XmlResourceParser.END_TAG) {
138 throw new IllegalStateException("Expected end of domain tag");
139 }
140 }
141
142 /**
143 * Initialize our Flutter config values by obtaining them from the manifest XML file, falling back
144 * to default values.
145 */
146 @NonNull
147 public static FlutterApplicationInfo load(@NonNull Context applicationContext) {
148 ApplicationInfo appInfo = getApplicationInfo(applicationContext);
149 return new FlutterApplicationInfo(
150 getString(appInfo.metaData, PUBLIC_AOT_SHARED_LIBRARY_NAME),
151 getString(appInfo.metaData, PUBLIC_VM_SNAPSHOT_DATA_KEY),
152 getString(appInfo.metaData, PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY),
153 getString(appInfo.metaData, PUBLIC_FLUTTER_ASSETS_DIR_KEY),
154 getNetworkPolicy(appInfo, applicationContext),
155 appInfo.nativeLibraryDir,
156 getBoolean(appInfo.metaData, PUBLIC_AUTOMATICALLY_REGISTER_PLUGINS_METADATA_KEY, true));
157 }
158}
static FlutterApplicationInfo load(@NonNull Context applicationContext)
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32