• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.nfc;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.os.Binder;
22 import android.os.BugreportManager;
23 import android.os.BugreportParams;
24 import android.os.Build;
25 import android.os.SystemClock;
26 import android.util.Log;
27 
28 /**
29  * A class to trigger bugreport and other logs for Nfc related failures
30  */
31 public class NfcDiagnostics {
32     private static final String TAG = "NfcDiagnostics";
33     private static final int MS_IN_HOUR = 60 * 60 * 1000;
34     public static final int DEFAULT_BUG_REPORT_MIN_INTERVAL_MS = 24 * MS_IN_HOUR;
35 
36     private final Context mContext;
37     private final SystemBuildProperties mSystemBuildProperties;
38     private long mLastBugReportTimeMs;
39 
40     static class SystemBuildProperties {
41         /** @return if it is an eng build. */
isEngBuild()42         public boolean isEngBuild() {
43             return Build.TYPE.equals("eng");
44         }
45 
46         /** @return if it is an userdebug build. */
isUserdebugBuild()47         public boolean isUserdebugBuild() {
48             return Build.TYPE.equals("userdebug");
49         }
50 
51         /** @return if it is a normal user build. */
isUserBuild()52         public boolean isUserBuild() {
53             return Build.TYPE.equals("user");
54         }
55     }
56 
NfcDiagnostics(Context context)57     public NfcDiagnostics(Context context) {
58         mContext = context;
59         mSystemBuildProperties = new SystemBuildProperties();
60     }
61 
62     /**
63      * Take a bug report if it is in user debug build and there is no recent bug
64      * report
65      */
takeBugReport(String bugTitle, String description)66     public void takeBugReport(String bugTitle, String description) {
67         if (!mSystemBuildProperties.isUserdebugBuild()) {
68             Log.d(TAG, "Skip bugreport because it can be triggered only in userDebug build");
69             return;
70         }
71         long currentTimeMs = getElapsedSinceBootMillis();
72         long timeSinceLastUploadMs = currentTimeMs - mLastBugReportTimeMs;
73 
74         if (timeSinceLastUploadMs < DEFAULT_BUG_REPORT_MIN_INTERVAL_MS
75                 && mLastBugReportTimeMs > 0) {
76             Log.d(TAG, "Bugreport was filed recently, Skip " + bugTitle);
77             return;
78         }
79 
80         if (!takeBugreportThroughBetterBug(bugTitle, description)) {
81             takeBugreportThroughBugreportManager(bugTitle, description);
82         }
83     }
84 
takeBugreportThroughBetterBug(String bugTitle, String description)85     private boolean takeBugreportThroughBetterBug(String bugTitle, String description) {
86         Intent launchBetterBugIntent = new BetterBugIntentBuilder()
87                 .setIssueTitle(bugTitle)
88                 .setHappenedTimestamp(System.currentTimeMillis())
89                 .setAdditionalComment(description)
90                 .build();
91         boolean isIntentUnSafe = mContext
92                 .getPackageManager().queryIntentActivities(launchBetterBugIntent, 0)
93                 .isEmpty();
94         if (isIntentUnSafe) {
95             Log.d(TAG, "intent is unsafe and skip bugreport from betterBug: " + bugTitle);
96             return false;
97         }
98 
99         long identity = Binder.clearCallingIdentity();
100         try {
101             launchBetterBugIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
102             mContext.startActivity(launchBetterBugIntent);
103             Log.d(TAG, "Taking the bugreport through betterBug: " + bugTitle);
104             mLastBugReportTimeMs = getElapsedSinceBootMillis();
105             return true;
106         } catch (RuntimeException e) {
107             Log.e(TAG, "Error taking bugreport: " + e);
108             return false;
109         } finally {
110             Binder.restoreCallingIdentity(identity);
111         }
112     }
113 
getElapsedSinceBootMillis()114     private long getElapsedSinceBootMillis() {
115         return SystemClock.elapsedRealtime();
116     }
117 
takeBugreportThroughBugreportManager(String bugTitle, String description)118     private boolean takeBugreportThroughBugreportManager(String bugTitle, String description) {
119         BugreportManager bugreportManager = mContext.getSystemService(BugreportManager.class);
120         BugreportParams params = new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
121         try {
122             bugreportManager.requestBugreport(params, bugTitle, description);
123             Log.d(TAG, "Taking the bugreport through bugreportManager: " + bugTitle);
124             mLastBugReportTimeMs = getElapsedSinceBootMillis();
125             return true;
126         } catch (RuntimeException e) {
127             Log.e(TAG, "Error taking bugreport: ", e);
128             return false;
129         }
130     }
131 
132     /**
133      * @return the last time when the bug report is taken
134      */
getLastBugReportTimeMs()135     long getLastBugReportTimeMs() {
136         return mLastBugReportTimeMs;
137     }
138 
139     /**
140      * Builder for communicating with the betterbug.
141      */
142     private static class BetterBugIntentBuilder {
143 
144         private static final String ACTION_FILE_BUG_DEEPLINK =
145                 "com.google.android.apps.betterbug.intent.FILE_BUG_DEEPLINK";
146         private static final boolean DEFAULT_AUTO_UPLOAD_ENABLED = false;
147         private static final boolean DEFAULT_BUGREPORT_REQUIRED = true;
148         private static final String DEFAULT_BUG_ASSIGNEE = "android-nfc-team@google.com";
149         private static final long DEFAULT_COMPONENT_ID = 48448L;
150 
151         private static final String EXTRA_DEEPLINK = "EXTRA_DEEPLINK";
152         private static final String EXTRA_ISSUE_TITLE = "EXTRA_ISSUE_TITLE";
153         private static final String EXTRA_DEEPLINK_SILENT = "EXTRA_DEEPLINK_SILENT";
154         private static final String EXTRA_ADDITIONAL_COMMENT = "EXTRA_ADDITIONAL_COMMENT";
155         private static final String EXTRA_REQUIRE_BUGREPORT = "EXTRA_REQUIRE_BUGREPORT";
156         private static final String EXTRA_HAPPENED_TIME = "EXTRA_HAPPENED_TIME";
157         private static final String EXTRA_BUG_ASSIGNEE = "EXTRA_BUG_ASSIGNEE";
158         private static final String EXTRA_COMPONENT_ID = "EXTRA_COMPONENT_ID";
159 
160         private final Intent mBetterBugIntent;
161 
BetterBugIntentBuilder()162         BetterBugIntentBuilder() {
163             mBetterBugIntent = new Intent().setAction(ACTION_FILE_BUG_DEEPLINK)
164                     .putExtra(EXTRA_DEEPLINK, true);
165             setAutoUpload(DEFAULT_AUTO_UPLOAD_ENABLED);
166             setBugreportRequired(DEFAULT_BUGREPORT_REQUIRED);
167             setBugAssignee(DEFAULT_BUG_ASSIGNEE);
168             setComponentId(DEFAULT_COMPONENT_ID);
169         }
170 
setIssueTitle(String title)171         public BetterBugIntentBuilder setIssueTitle(String title) {
172             mBetterBugIntent.putExtra(EXTRA_ISSUE_TITLE, title);
173             return this;
174         }
175 
setAutoUpload(boolean autoUploadEnabled)176         public BetterBugIntentBuilder setAutoUpload(boolean autoUploadEnabled) {
177             mBetterBugIntent.putExtra(EXTRA_DEEPLINK_SILENT, autoUploadEnabled);
178             return this;
179         }
180 
setComponentId(long componentId)181         public BetterBugIntentBuilder setComponentId(long componentId) {
182             mBetterBugIntent.putExtra(EXTRA_COMPONENT_ID, componentId);
183             return this;
184         }
185 
setBugreportRequired(boolean isBugreportRequired)186         public BetterBugIntentBuilder setBugreportRequired(boolean isBugreportRequired) {
187             mBetterBugIntent.putExtra(EXTRA_REQUIRE_BUGREPORT, isBugreportRequired);
188             return this;
189         }
190 
setHappenedTimestamp(long happenedTimeSinceEpochMs)191         public BetterBugIntentBuilder setHappenedTimestamp(long happenedTimeSinceEpochMs) {
192             mBetterBugIntent.putExtra(EXTRA_HAPPENED_TIME, happenedTimeSinceEpochMs);
193             return this;
194         }
195 
setAdditionalComment(String additionalComment)196        public BetterBugIntentBuilder setAdditionalComment(String additionalComment) {
197             mBetterBugIntent.putExtra(EXTRA_ADDITIONAL_COMMENT, additionalComment);
198             return this;
199         }
200 
setBugAssignee(String assignee)201         public BetterBugIntentBuilder setBugAssignee(String assignee) {
202             mBetterBugIntent.putExtra(EXTRA_BUG_ASSIGNEE, assignee);
203             return this;
204         }
205 
build()206         public Intent build() {
207             return mBetterBugIntent;
208         }
209     }
210 }
211