/* ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ package com.android.development; import android.app.Activity; import android.os.Bundle; import android.os.SystemProperties; import android.provider.Checkin; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneFactory; import android.telephony.ServiceState; import android.text.format.DateFormat; import static com.android.internal.util.CharSequences.forAsciiBytes; import android.util.Log; import android.view.View.OnClickListener; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.google.android.collect.Maps; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import java.net.Socket; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Map; /** * Report radio issues to the StatisticsService. */ public class RadioIssueReport extends Activity { private static final String TAG = "RadioIssue"; private static final int HEADER_SIZE = 24; private static final String RADIO_BUFFER_OPTIONS = "-b radio\n-d\n"; /** List of system properties to snapshot. */ private static String[] SYSTEM_PROPERTIES = { "net.gsm.radio-reset", "net.gsm.attempt-gprs", "net.gsm.succeed-gprs", "net.gsm.disconnect", "net.ppp.sent", "net.ppp.received", "gsm.version.baseband", "gsm.version.ril-impl", }; private Button mSubmitButton; private EditText mReportText; private ServiceState mServiceState; private Phone.State mPhoneState; private int mSignalStrength; private Phone.DataState mDataState; private String mRadioLog; /** Snapshot of interesting variables relevant to the radio. */ private Map<String, String> mRadioState; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.radio_issue); initSubmitButton(); initReportText(); mRadioState = snapState(); } /** * @return a snapshot of phone state variables to report. */ private static Map<String, String> snapState() { Map<String, String> state = Maps.newHashMap(); // Capture a bunch of system properties for (String property: SYSTEM_PROPERTIES) { String value = SystemProperties.get(property); state.put(property, SystemProperties.get(property)); } Phone phone = PhoneFactory.getDefaultPhone(); state.put("phone-data", phone.getDataConnectionState().toString()); state.put("phone-service", phone.getServiceState().toString()); state.put("phone-signal", String.valueOf(phone.getSignalStrengthASU())); state.put("phone-state", phone.getState().toString()); try { state.put("radio-log", getRadioLog()); } catch (IOException e) { Log.e(TAG, "Error reading radio log", e); } return state; } private void initSubmitButton() { mSubmitButton = (Button) findViewById(R.id.submit); mSubmitButton.setOnClickListener(mSubmitButtonHandler); } private void initReportText() { mReportText = (EditText) findViewById(R.id.report_text); mReportText.requestFocus(); } OnClickListener mSubmitButtonHandler = new OnClickListener() { public void onClick(View v) { // Include the user-supplied report text. mRadioState.put("user-report", mReportText.getText().toString()); // Dump the state variables directly into the report. Checkin.logEvent(getContentResolver(), Checkin.Events.Tag.RADIO_BUG_REPORT, mRadioState.toString()); finish(); } }; // Largely stolen from LogViewer.java private static String getRadioLog() throws IOException { Socket sock = new Socket("127.0.0.1", 5040); DataInputStream in = new DataInputStream(sock.getInputStream()); StringBuilder log = new StringBuilder(); // Set options sock.getOutputStream().write(RADIO_BUFFER_OPTIONS.getBytes()); sock.getOutputStream().write('\n'); sock.getOutputStream().write('\n'); // Read in the log try { Calendar cal = new GregorianCalendar(); while (true) { int length = in.readInt(); long when = (long)in.readInt(); byte[] bytes = new byte[length-4]; in.readFully(bytes); int tagEnd = next0(bytes, HEADER_SIZE-4); int fileEnd = next0(bytes, tagEnd + 1); int messageEnd = next0(bytes, fileEnd + 1); CharSequence tag = forAsciiBytes(bytes, HEADER_SIZE-4, tagEnd); CharSequence message = forAsciiBytes(bytes, fileEnd + 1, messageEnd); cal.setTimeInMillis(when*1000); log.append(DateFormat.format("MM-dd kk:mm:ss ", cal)); log.append(tag) .append(": ") .append(message) .append("\n"); } } catch (EOFException e) { Log.d(TAG, "reached end of stream"); } return log.toString(); } private static int next0(byte[] bytes, int start) { for (int current = start; current < bytes.length; current++) { if (bytes[current] == 0) return current; } return bytes.length; } }