1 /*
2  * Copyright (C) 2018 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.cts.install.lib.testapp;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.res.Resources;
23 import android.media.AudioFocusRequest;
24 import android.media.AudioManager;
25 import android.os.Process;
26 import android.system.ErrnoException;
27 import android.system.Os;
28 
29 import java.io.File;
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.io.PrintWriter;
33 import java.nio.charset.StandardCharsets;
34 import java.util.Scanner;
35 
36 /**
37  * A broadcast receiver to handle intents sent by tests.
38  *
39  * PROCESS_USER_DATA: check for and update user app data version and user handle compatibility.
40  * GET_USER_DATA_VERSION: return user app data version
41  * REQUEST_AUDIO_FOCUS: request audio focus
42  * ABANDON_AUDIO_FOCUS: abandon audio focus
43  */
44 public class ProcessBroadcast extends BroadcastReceiver {
45 
46     /**
47      * Exception thrown in case of issue with user data.
48      */
49     public static class UserDataException extends Exception {
UserDataException(String message)50         public UserDataException(String message) {
51             super(message);
52         }
53 
UserDataException(String message, Throwable cause)54         public UserDataException(String message, Throwable cause) {
55            super(message, cause);
56         }
57     }
58 
59     @Override
onReceive(Context context, Intent intent)60     public void onReceive(Context context, Intent intent) {
61         final String action = intent.getAction();
62         if ("PROCESS_USER_DATA".equals(action)) {
63             try {
64                 processUserData(context);
65                 setResultCode(1);
66             } catch (UserDataException e) {
67                 setResultCode(0);
68                 setResultData(e.getMessage());
69             }
70         } else if ("GET_USER_DATA_VERSION".equals(action)) {
71             setResultCode(getUserDataVersion(context));
72         } else if ("REQUEST_AUDIO_FOCUS".equals(action)) {
73             requestAudioFocus(context);
74         } else if ("ABANDON_AUDIO_FOCUS".equals(action)) {
75             abandonAudioFocus(context);
76         }
77     }
78 
requestAudioFocus(Context context)79     private void requestAudioFocus(Context context) {
80         AudioManager audioManager = context.getSystemService(AudioManager.class);
81         final AudioFocusRequest afr =
82                 new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).build();
83         audioManager.requestAudioFocus(afr);
84         setResultCode(0);
85     }
86 
abandonAudioFocus(Context context)87     private void abandonAudioFocus(Context context) {
88         AudioManager audioManager = context.getSystemService(AudioManager.class);
89         final AudioFocusRequest afr =
90                 new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).build();
91         audioManager.abandonAudioFocusRequest(afr);
92         setResultCode(0);
93     }
94 
95     /**
96      * Update the app's user data version to match the app version, and confirm
97      * the user data is for the correct user.
98      *
99      * @param context The application context.
100      * @throws UserDataException in case of problems with app user data.
101      */
processUserData(Context context)102     public void processUserData(Context context) throws UserDataException {
103         Resources res = context.getResources();
104         String packageName = context.getPackageName();
105 
106         String userHandle = Process.myUserHandle().toString();
107 
108         int appVersionId = res.getIdentifier("app_version", "integer", packageName);
109         int appVersion = res.getInteger(appVersionId);
110 
111         int splitVersionId = res.getIdentifier("split_version", "integer", packageName);
112         int splitVersion = res.getInteger(splitVersionId);
113 
114         // Make sure the app version and split versions are compatible.
115         if (appVersion != splitVersion) {
116             throw new UserDataException("Split version " + splitVersion
117                     + " does not match app version " + appVersion);
118         }
119 
120         // Read the version of the app's user data and ensure it is compatible
121         // with our version of the application. Also ensure that the user data is
122         // for the correct user.
123         File versionFile = new File(context.getFilesDir(), "userdata.txt");
124         try {
125             Scanner s = new Scanner(versionFile);
126             int userDataVersion = s.nextInt();
127             s.nextLine();
128 
129             if (userDataVersion > appVersion) {
130                 throw new UserDataException("User data is from version " + userDataVersion
131                         + ", which is not compatible with this version " + appVersion
132                         + " of the RollbackTestApp");
133             }
134 
135             String readUserHandle = s.nextLine();
136             s.close();
137 
138             if (!readUserHandle.equals(userHandle)) {
139                 throw new UserDataException("User handle expected to be: " + userHandle
140                         + ", but was actually " + readUserHandle);
141             }
142 
143             int xattrVersion = Integer.valueOf(
144                     new String(Os.getxattr(versionFile.getAbsolutePath(), "user.test")));
145 
146             if (xattrVersion > appVersion) {
147                 throw new UserDataException("xattr data is from version " + xattrVersion
148                         + ", which is not compatible with this version " + appVersion
149                         + " of the RollbackTestApp");
150             }
151         } catch (FileNotFoundException e) {
152             // No problem. This is a fresh install of the app or the user data
153             // has been wiped.
154         } catch (ErrnoException e) {
155             throw new UserDataException("Error while retrieving xattr.", e);
156         }
157 
158         // Record the current version of the app in the user data.
159         try {
160             PrintWriter pw = new PrintWriter(versionFile);
161             pw.println(appVersion);
162             pw.println(userHandle);
163             pw.close();
164             Os.setxattr(versionFile.getAbsolutePath(), "user.test",
165                     Integer.toString(appVersion).getBytes(StandardCharsets.UTF_8), 0);
166         } catch (IOException e) {
167             throw new UserDataException("Unable to write user data.", e);
168         } catch (ErrnoException e) {
169             throw new UserDataException("Unable to set xattr.", e);
170         }
171     }
172 
173     /**
174      * Return the app's user data version or -1 if userdata.txt doesn't exist.
175      */
getUserDataVersion(Context context)176     private int getUserDataVersion(Context context) {
177         File versionFile = new File(context.getFilesDir(), "userdata.txt");
178         try (Scanner s = new Scanner(versionFile);) {
179             int dataVersion = s.nextInt();
180             return dataVersion;
181         } catch (FileNotFoundException e) {
182             // No problem. This is a fresh install of the app or the user data
183             // has been wiped.
184             return -1;
185         }
186     }
187 }
188