├── .gitignore ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tcl │ │ └── navigator │ │ └── hostchart │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tcl │ │ │ └── navigator │ │ │ └── hostchart │ │ │ ├── activity │ │ │ └── MainActivity.java │ │ │ ├── base │ │ │ └── MyApplication.java │ │ │ ├── receiver │ │ │ ├── OpenDevicesReceiver.java │ │ │ └── UsbDetachedReceiver.java │ │ │ └── utils │ │ │ └── CrashHandler.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── tcl │ └── navigator │ └── hostchart │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.tcl.navigator.hostchart" 8 | minSdkVersion 15 9 | targetSdkVersion 24 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:24.1.1' 28 | testCompile 'junit:junit:4.12' 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Administrator.WIN-20160308XVA\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/tcl/navigator/hostchart/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.tcl.navigator.hostchart; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.tcl.navigator.hostchart", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/tcl/navigator/hostchart/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.tcl.navigator.hostchart.activity; 2 | 3 | import android.app.PendingIntent; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.hardware.usb.UsbConstants; 8 | import android.hardware.usb.UsbDevice; 9 | import android.hardware.usb.UsbDeviceConnection; 10 | import android.hardware.usb.UsbEndpoint; 11 | import android.hardware.usb.UsbInterface; 12 | import android.hardware.usb.UsbManager; 13 | import android.os.Bundle; 14 | import android.os.Handler; 15 | import android.os.Message; 16 | import android.os.SystemClock; 17 | import android.support.v7.app.AppCompatActivity; 18 | import android.text.TextUtils; 19 | import android.view.View; 20 | import android.widget.Button; 21 | import android.widget.EditText; 22 | import android.widget.TextView; 23 | 24 | import com.tcl.navigator.hostchart.R; 25 | import com.tcl.navigator.hostchart.base.MyApplication; 26 | import com.tcl.navigator.hostchart.receiver.OpenDevicesReceiver; 27 | import com.tcl.navigator.hostchart.receiver.UsbDetachedReceiver; 28 | 29 | import java.util.Collection; 30 | import java.util.HashMap; 31 | import java.util.concurrent.ExecutorService; 32 | import java.util.concurrent.Executors; 33 | 34 | /** 35 | * 车机端 36 | *yaojun 37 | */ 38 | 39 | public class MainActivity extends AppCompatActivity implements UsbDetachedReceiver.UsbDetachedListener, OpenDevicesReceiver.OpenDevicesListener, View.OnClickListener { 40 | 41 | private static final int CONNECTED_SUCCESS = 0; 42 | private static final int RECEIVER_MESSAGE_SUCCESS = 1; 43 | private static final int SEND_MESSAGE_SUCCESS = 2; 44 | private static final String USB_ACTION = "com.tcl.navigator.hostchart"; 45 | private TextView mLog; 46 | private EditText mMessage; 47 | private UsbDetachedReceiver mUsbDetachedReceiver; 48 | private ExecutorService mThreadPool; 49 | private UsbManager mUsbManager; 50 | private OpenDevicesReceiver mOpenDevicesReceiver; 51 | private TextView mError; 52 | private UsbDeviceConnection mUsbDeviceConnection; 53 | private UsbEndpoint mUsbEndpointOut; 54 | private UsbEndpoint mUsbEndpointIn; 55 | private boolean mToggle = true; 56 | private Button mSendMessage; 57 | private boolean isDetached = false; 58 | private byte[] mBytes = new byte[1024]; 59 | private boolean isReceiverMessage = true; 60 | private UsbInterface mUsbInterface; 61 | private StringBuffer mStringBuffer = new StringBuffer(); 62 | private Context mContext; 63 | 64 | private final Handler mHandler = new Handler() { 65 | @Override 66 | public void handleMessage(Message msg) { 67 | switch (msg.what) { 68 | case CONNECTED_SUCCESS://车机和手机连接成功 69 | mError.setText(""); 70 | mSendMessage.setEnabled(true); 71 | loopReceiverMessage(); 72 | break; 73 | 74 | case RECEIVER_MESSAGE_SUCCESS://成功接受到数据 75 | mLog.setText(mStringBuffer.toString()); 76 | break; 77 | 78 | case SEND_MESSAGE_SUCCESS://成功发送数据 79 | mMessage.setText(""); 80 | break; 81 | } 82 | } 83 | }; 84 | 85 | @Override 86 | protected void onCreate(Bundle savedInstanceState) { 87 | super.onCreate(savedInstanceState); 88 | setContentView(R.layout.activity_main); 89 | 90 | init(); 91 | } 92 | 93 | private void init() { 94 | initView(); 95 | initListener(); 96 | initData(); 97 | } 98 | 99 | private void initView() { 100 | mLog = (TextView) findViewById(R.id.log); 101 | mError = (TextView) findViewById(R.id.error); 102 | mMessage = (EditText) findViewById(R.id.message); 103 | mSendMessage = (Button) findViewById(R.id.sendmessage); 104 | } 105 | 106 | private void initListener() { 107 | mSendMessage.setOnClickListener(this); 108 | } 109 | 110 | private void initData() { 111 | mContext = getApplicationContext(); 112 | mSendMessage.setEnabled(false); 113 | mUsbDetachedReceiver = new UsbDetachedReceiver(this); 114 | IntentFilter intentFilter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED); 115 | registerReceiver(mUsbDetachedReceiver, intentFilter); 116 | 117 | mThreadPool = Executors.newFixedThreadPool(5); 118 | 119 | mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE); 120 | 121 | openDevices(); 122 | } 123 | 124 | /** 125 | * 打开设备 , 让车机和手机端连起来 126 | */ 127 | private void openDevices() { 128 | PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(USB_ACTION), 0); 129 | IntentFilter intentFilter = new IntentFilter(USB_ACTION); 130 | mOpenDevicesReceiver = new OpenDevicesReceiver(this); 131 | registerReceiver(mOpenDevicesReceiver, intentFilter); 132 | 133 | //列举设备(手机) 134 | HashMap deviceList = mUsbManager.getDeviceList(); 135 | if (deviceList != null) { 136 | for (UsbDevice usbDevice : deviceList.values()) { 137 | int productId = usbDevice.getProductId(); 138 | if (productId != 377 && productId != 7205) { 139 | if (mUsbManager.hasPermission(usbDevice)) { 140 | initAccessory(usbDevice); 141 | } else { 142 | mUsbManager.requestPermission(usbDevice, pendingIntent); 143 | } 144 | } 145 | } 146 | } else { 147 | mError.setText("请连接USB"); 148 | } 149 | } 150 | 151 | /** 152 | * 发送命令 , 让手机进入Accessory模式 153 | * 154 | * @param usbDevice 155 | */ 156 | private void initAccessory(UsbDevice usbDevice) { 157 | UsbDeviceConnection usbDeviceConnection = mUsbManager.openDevice(usbDevice); 158 | if (usbDeviceConnection == null) { 159 | mError.setText("请连接USB"); 160 | return; 161 | } 162 | 163 | //根据AOA协议打开Accessory模式 164 | initStringControlTransfer(usbDeviceConnection, 0, "Google, Inc."); // MANUFACTURER 165 | initStringControlTransfer(usbDeviceConnection, 1, "AccessoryChat"); // MODEL 166 | initStringControlTransfer(usbDeviceConnection, 2, "Accessory Chat"); // DESCRIPTION 167 | initStringControlTransfer(usbDeviceConnection, 3, "1.0"); // VERSION 168 | initStringControlTransfer(usbDeviceConnection, 4, "http://www.android.com"); // URI 169 | initStringControlTransfer(usbDeviceConnection, 5, "0123456789"); // SERIAL 170 | usbDeviceConnection.controlTransfer(0x40, 53, 0, 0, new byte[]{}, 0, 100); 171 | usbDeviceConnection.close(); 172 | MyApplication.printLogDebug("initAccessory success"); 173 | initDevice(); 174 | } 175 | 176 | private void initStringControlTransfer(UsbDeviceConnection deviceConnection, int index, String string) { 177 | deviceConnection.controlTransfer(0x40, 52, 0, index, string.getBytes(), string.length(), 100); 178 | } 179 | 180 | /** 181 | * 初始化设备(手机) , 当手机进入Accessory模式后 , 手机的PID会变为Google定义的2个常量值其中的一个 , 182 | */ 183 | private void initDevice() { 184 | mThreadPool.execute(new Runnable() { 185 | @Override 186 | public void run() { 187 | while (mToggle) { 188 | SystemClock.sleep(1000); 189 | HashMap deviceList = mUsbManager.getDeviceList(); 190 | Collection values = deviceList.values(); 191 | if (!values.isEmpty()) { 192 | for (UsbDevice usbDevice : values) { 193 | int productId = usbDevice.getProductId(); 194 | if (productId == 0x2D00 || productId == 0x2D01) { 195 | if (mUsbManager.hasPermission(usbDevice)) { 196 | mUsbDeviceConnection = mUsbManager.openDevice(usbDevice); 197 | if (mUsbDeviceConnection != null) { 198 | mUsbInterface = usbDevice.getInterface(0); 199 | int endpointCount = mUsbInterface.getEndpointCount(); 200 | for (int i = 0; i < endpointCount; i++) { 201 | UsbEndpoint usbEndpoint = mUsbInterface.getEndpoint(i); 202 | if (usbEndpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { 203 | if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_OUT) { 204 | mUsbEndpointOut = usbEndpoint; 205 | } else if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_IN) { 206 | mUsbEndpointIn = usbEndpoint; 207 | } 208 | } 209 | } 210 | if (mUsbEndpointOut != null && mUsbEndpointIn != null) { 211 | MyApplication.printLogDebug("connected success"); 212 | mHandler.sendEmptyMessage(CONNECTED_SUCCESS); 213 | mToggle = false; 214 | isDetached = true; 215 | } 216 | } 217 | } else { 218 | mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(""), 0)); 219 | } 220 | } 221 | } 222 | } else { 223 | finish(); 224 | } 225 | } 226 | } 227 | }); 228 | } 229 | 230 | /** 231 | * 接受消息线程 , 此线程在设备(手机)初始化完成后 , 就一直循环接受消息 232 | */ 233 | private void loopReceiverMessage() { 234 | mThreadPool.execute(new Runnable() { 235 | @Override 236 | public void run() { 237 | SystemClock.sleep(1000); 238 | while (isReceiverMessage) { 239 | /** 240 | * 循环接受数据的地方 , 只接受byte数据类型的数据 241 | */ 242 | if (mUsbDeviceConnection != null && mUsbEndpointIn != null) { 243 | int i = mUsbDeviceConnection.bulkTransfer(mUsbEndpointIn, mBytes, mBytes.length, 3000); 244 | MyApplication.printLogDebug(i + ""); 245 | if (i > 0) { 246 | mStringBuffer.append(new String(mBytes, 0, i) + "\n"); 247 | mHandler.sendEmptyMessage(RECEIVER_MESSAGE_SUCCESS); 248 | } 249 | } 250 | } 251 | } 252 | }); 253 | } 254 | 255 | @Override 256 | public void usbDetached() { 257 | if (isDetached) { 258 | finish(); 259 | } 260 | } 261 | 262 | @Override 263 | public void openAccessoryModel(UsbDevice usbDevice) { 264 | initAccessory(usbDevice); 265 | } 266 | 267 | @Override 268 | public void openDevicesError() { 269 | mError.setText("USB连接错误"); 270 | } 271 | 272 | @Override 273 | public void onClick(View v) { 274 | final String messageContent = mMessage.getText().toString(); 275 | if (!TextUtils.isEmpty(messageContent)) { 276 | mThreadPool.execute(new Runnable() { 277 | @Override 278 | public void run() { 279 | /** 280 | * 发送数据的地方 , 只接受byte数据类型的数据 281 | */ 282 | int i = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut, messageContent.getBytes(), messageContent.getBytes().length, 3000); 283 | if (i > 0) {//大于0表示发送成功 284 | mHandler.sendEmptyMessage(SEND_MESSAGE_SUCCESS); 285 | } 286 | } 287 | }); 288 | } 289 | } 290 | 291 | @Override 292 | protected void onDestroy() { 293 | mHandler.removeCallbacksAndMessages(null); 294 | super.onDestroy(); 295 | 296 | if (mUsbDeviceConnection != null) { 297 | mUsbDeviceConnection.releaseInterface(mUsbInterface); 298 | mUsbDeviceConnection.close(); 299 | mUsbDeviceConnection = null; 300 | } 301 | mUsbEndpointIn = null; 302 | mUsbEndpointOut = null; 303 | mToggle = false; 304 | isReceiverMessage = false; 305 | mThreadPool.shutdownNow(); 306 | unregisterReceiver(mUsbDetachedReceiver); 307 | unregisterReceiver(mOpenDevicesReceiver); 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /app/src/main/java/com/tcl/navigator/hostchart/base/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.tcl.navigator.hostchart.base; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.tcl.navigator.hostchart.utils.CrashHandler; 7 | 8 | /** 9 | * Created by yaohui on 2017/3/3. 10 | */ 11 | 12 | public class MyApplication extends Application { 13 | 14 | private static final String TAG = "yaohui"; 15 | 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | 20 | CrashHandler.getInstance().init(this); 21 | } 22 | 23 | public static void printLogDebug(String logString) { 24 | Log.d(TAG, logString); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/tcl/navigator/hostchart/receiver/OpenDevicesReceiver.java: -------------------------------------------------------------------------------- 1 | package com.tcl.navigator.hostchart.receiver; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.hardware.usb.UsbDevice; 7 | import android.hardware.usb.UsbManager; 8 | 9 | /** 10 | * Created by yaohui on 2017/3/3. 11 | */ 12 | 13 | public class OpenDevicesReceiver extends BroadcastReceiver { 14 | 15 | private OpenDevicesListener mOpenDevicesListener; 16 | 17 | public OpenDevicesReceiver(OpenDevicesListener openDevicesListener) { 18 | mOpenDevicesListener = openDevicesListener; 19 | } 20 | 21 | @Override 22 | public void onReceive(Context context, Intent intent) { 23 | UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); 24 | if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { 25 | if (usbDevice != null) { 26 | mOpenDevicesListener.openAccessoryModel(usbDevice); 27 | } else { 28 | mOpenDevicesListener.openDevicesError(); 29 | } 30 | } else { 31 | mOpenDevicesListener.openDevicesError(); 32 | } 33 | } 34 | 35 | public interface OpenDevicesListener { 36 | /** 37 | * 打开Accessory模式 38 | * 39 | * @param usbDevice 40 | */ 41 | void openAccessoryModel(UsbDevice usbDevice); 42 | 43 | /** 44 | * 打开设备(手机)失败 45 | */ 46 | void openDevicesError(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/tcl/navigator/hostchart/receiver/UsbDetachedReceiver.java: -------------------------------------------------------------------------------- 1 | package com.tcl.navigator.hostchart.receiver; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | public class UsbDetachedReceiver extends BroadcastReceiver { 8 | 9 | private UsbDetachedListener mUsbDetachedListener; 10 | 11 | public UsbDetachedReceiver(UsbDetachedListener usbDetachedListener) { 12 | mUsbDetachedListener = usbDetachedListener; 13 | } 14 | 15 | @Override 16 | public void onReceive(Context context, Intent intent) { 17 | mUsbDetachedListener.usbDetached(); 18 | } 19 | 20 | public interface UsbDetachedListener { 21 | /** 22 | * usb断开连接 23 | */ 24 | void usbDetached(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/tcl/navigator/hostchart/utils/CrashHandler.java: -------------------------------------------------------------------------------- 1 | package com.tcl.navigator.hostchart.utils; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.os.Build; 7 | import android.os.Environment; 8 | import android.os.Process; 9 | 10 | import java.io.BufferedWriter; 11 | import java.io.File; 12 | import java.io.FileWriter; 13 | import java.io.IOException; 14 | import java.io.PrintWriter; 15 | import java.text.SimpleDateFormat; 16 | 17 | /** 18 | * Created by yaohui on 2017/4/13. 19 | */ 20 | 21 | public class CrashHandler implements Thread.UncaughtExceptionHandler { 22 | 23 | private static final String FILE_NAME = "crash"; 24 | private static final String FILE_NAME_SUFFIX = ".trace"; 25 | private Thread.UncaughtExceptionHandler mDefaulCrashHandler; 26 | private Context mContext; 27 | private SimpleDateFormat mSdf; 28 | 29 | private CrashHandler() { 30 | } 31 | 32 | public static CrashHandler getInstance() { 33 | return CrashHandlerHolder.CRASH_HANDLER; 34 | } 35 | 36 | private static class CrashHandlerHolder { 37 | private static final CrashHandler CRASH_HANDLER = new CrashHandler(); 38 | } 39 | 40 | public void init(Context context) { 41 | // mDefaulCrashHandler = Thread.getDefaultUncaughtExceptionHandler(); 42 | Thread.setDefaultUncaughtExceptionHandler(this); 43 | mContext = context.getApplicationContext(); 44 | mSdf = (SimpleDateFormat) SimpleDateFormat.getInstance(); 45 | mSdf.applyPattern("yyyy-MM-dd HH:mm:ss"); 46 | } 47 | 48 | @Override 49 | public void uncaughtException(Thread t, Throwable e) { 50 | try { 51 | dumpException(e); 52 | uploadExceptionToServer(); 53 | } catch (IOException e1) { 54 | e1.printStackTrace(); 55 | } 56 | 57 | e.printStackTrace(); 58 | if (mDefaulCrashHandler != null) 59 | mDefaulCrashHandler.uncaughtException(t, e); 60 | else 61 | Process.killProcess(Process.myPid()); 62 | } 63 | 64 | private void dumpException(Throwable ex) throws IOException { 65 | String path = null; 66 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) 67 | path = mContext.getExternalFilesDir(null).getAbsolutePath() + "/crash/log"; 68 | else 69 | path = mContext.getFilesDir().getAbsolutePath() + "/crash/log"; 70 | File dir = new File(path); 71 | if (!dir.exists()) 72 | dir.mkdirs(); 73 | long current = System.currentTimeMillis(); 74 | String time = mSdf.format(current); 75 | File file = new File(path + File.separator + FILE_NAME + time + FILE_NAME_SUFFIX); 76 | try { 77 | PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file))); 78 | pw.println(time); 79 | dumpPhoneInfo(pw); 80 | pw.println(); 81 | ex.printStackTrace(pw); 82 | pw.close(); 83 | } catch (PackageManager.NameNotFoundException e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | 88 | private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException { 89 | PackageManager pm = mContext.getPackageManager(); 90 | PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES); 91 | pw.print("App Version: "); 92 | pw.print(pi.versionName); 93 | pw.print('_'); 94 | pw.println(pi.versionCode); 95 | 96 | //Android版本号 97 | pw.print("OS Version: "); 98 | pw.print(Build.VERSION.RELEASE); 99 | pw.print('_'); 100 | pw.println(Build.VERSION.SDK_INT); 101 | 102 | //手机制造商 103 | pw.print("Vendor: "); 104 | pw.println(Build.MANUFACTURER); 105 | 106 | //手机型号 107 | pw.print("Model: "); 108 | pw.println(Build.MODEL); 109 | 110 | //CPU架构 111 | pw.print("CPU ABI: "); 112 | pw.println(Build.CPU_ABI); 113 | } 114 | 115 | private void uploadExceptionToServer() { 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 21 | 22 | 27 | 28 | 29 | 34 | 35 | 39 | 40 |