1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bedstead.nene.packages;
18 
19 import android.content.ComponentName;
20 import android.text.TextUtils;
21 
22 import com.android.bedstead.nene.TestApis;
23 import com.android.bedstead.nene.annotations.Experimental;
24 import com.android.bedstead.nene.exceptions.AdbException;
25 import com.android.bedstead.nene.exceptions.NeneException;
26 import com.android.bedstead.nene.users.UserReference;
27 import com.android.bedstead.nene.utils.ShellCommand;
28 
29 import java.util.Objects;
30 
31 /**
32  * A representation of a component on device which may or may not exist.
33  */
34 @Experimental
35 public class ComponentReference {
36 
37     final Package mPackage;
38     final String mClassName;
39 
40     private static final String CONFIG_KEY_RESOLVER_ACTIVITY = "config_customResolverActivity";
41     private static final String DEFAULT_STRING_TYPE = "string";
42     private static final String DEFAULT_PACKAGE_ANDROID = "android";
43     private static final ComponentReference DEFAULT_RESOLVER_ACTIVITY =
44             new ComponentReference(TestApis.packages().find(DEFAULT_PACKAGE_ANDROID),
45                     "com.android.internal.app.ResolverActivity");
46 
47     /** See {@link ComponentName#unflattenFromString(String)}. */
unflattenFromString(String string)48     public static ComponentReference unflattenFromString(String string) {
49         return new ComponentReference(
50                 Objects.requireNonNull(ComponentName.unflattenFromString(string)));
51     }
52 
ComponentReference(Package packageName, String className)53     public ComponentReference(Package packageName, String className) {
54         mPackage = packageName;
55         mClassName = className;
56     }
57 
ComponentReference(ComponentName component)58     public ComponentReference(ComponentName component) {
59         this(new Package(component.getPackageName()), component.getClassName());
60     }
61 
62     /**
63      * Get the {@link Package} for this component.
64      */
pkg()65     public Package pkg() {
66         return mPackage;
67     }
68 
69     /**
70      * Get the class for this component.
71      */
className()72     public String className() {
73         return mClassName;
74     }
75 
76     /**
77      * Get this component as a {@link ComponentName}.
78      */
componentName()79     public ComponentName componentName() {
80         return new ComponentName(mPackage.packageName(), mClassName);
81     }
82 
83     /**
84      * Enable this component for the given {@link UserReference}.
85      */
enable(UserReference user)86     public ComponentReference enable(UserReference user) {
87         try {
88             ShellCommand.builderForUser(user, "pm enable")
89                     .addOperand(mPackage.packageName() + "/" + mClassName)
90                     .validate(o -> o.contains("new state"))
91                     .execute();
92         } catch (AdbException e) {
93             throw new NeneException("Error enabling component " + this + " for user " + user, e);
94         }
95         return this;
96     }
97 
98     /**
99      * See {@link ComponentName#flattenToString()}.
100      */
flattenToString()101     public String flattenToString() {
102         return componentName().flattenToString();
103     }
104 
105     /**
106      * Enable this component for the instrumented user.
107      */
enable()108     public ComponentReference enable() {
109         return enable(TestApis.users().instrumented());
110     }
111 
112     /**
113      * Disable this component for the given {@link UserReference}.
114      */
disable(UserReference user)115     public ComponentReference disable(UserReference user) {
116         try {
117             ShellCommand.builderForUser(user, "pm disable")
118                     .addOperand(mPackage.packageName() + "/" + mClassName)
119                     .validate(o -> o.contains("new state"))
120                     .execute();
121         } catch (AdbException e) {
122             throw new NeneException("Error disabling component " + this + " for user " + user, e);
123         }
124         return this;
125     }
126 
127     /**
128      * Disable this component for the instrumented user.
129      */
disable()130     public ComponentReference disable() {
131         return disable(TestApis.users().instrumented());
132     }
133 
134     /**
135      * Checks if the activity is a {@link com.android.internal.app.ResolverActivity}
136      */
isResolver()137     public boolean isResolver() {
138         return equals(getResolverActivity());
139     }
140 
getResolverActivity()141     private ComponentReference getResolverActivity() {
142         String resolverActivity = TestApis.resources().system().getString(
143                 /* name= */ CONFIG_KEY_RESOLVER_ACTIVITY,
144                 /* defType= */ DEFAULT_STRING_TYPE,
145                 /* defPackage= */ DEFAULT_PACKAGE_ANDROID);
146         if (TextUtils.isEmpty(resolverActivity)) {
147             return DEFAULT_RESOLVER_ACTIVITY;
148         }
149 
150         return ComponentReference.unflattenFromString(resolverActivity);
151     }
152 
153     @Override
hashCode()154     public int hashCode() {
155         return componentName().hashCode();
156     }
157 
158     @Override
equals(Object obj)159     public boolean equals(Object obj) {
160         if (!(obj instanceof ComponentReference)) {
161             return false;
162         }
163 
164         ComponentReference other = (ComponentReference) obj;
165         return other.componentName().equals(componentName());
166     }
167 
168     @Override
toString()169     public String toString() {
170         StringBuilder stringBuilder = new StringBuilder("ComponentReference{");
171         stringBuilder.append("package=" + mPackage);
172         stringBuilder.append(", component=" + mClassName);
173         stringBuilder.append("}");
174         return stringBuilder.toString();
175     }
176 }
177