From acc5537160f712bacf5c92102e9aa03838429e01 Mon Sep 17 00:00:00 2001
From: Pawit Pornkitprasan
Date: Tue, 18 Oct 2011 08:49:11 +0700
Subject: Add SamsungServiceMode (1/4)
This is an interface to the radio's control interface. The application is
accessable via *#*#197328640#*#* (or an easier to remember submenu of it is
accessable via *#*#0011#*#* and use back to get to the main menu).
Change-Id: Ie43f54003d9e683e25f05fa6e7f57711edef9e7f
---
SamsungServiceMode/Android.mk | 14 ++
SamsungServiceMode/AndroidManifest.xml | 118 +++++++++
SamsungServiceMode/res/layout/list_item.xml | 7 +
SamsungServiceMode/res/layout/main.xml | 12 +
SamsungServiceMode/res/values/strings.xml | 6 +
.../samsungservicemode/OemCommands.java | 110 ++++++++
.../SamsungServiceModeActivity.java | 277 +++++++++++++++++++++
.../SecretBroadcastReceiver.java | 19 ++
8 files changed, 563 insertions(+)
create mode 100644 SamsungServiceMode/Android.mk
create mode 100644 SamsungServiceMode/AndroidManifest.xml
create mode 100644 SamsungServiceMode/res/layout/list_item.xml
create mode 100644 SamsungServiceMode/res/layout/main.xml
create mode 100644 SamsungServiceMode/res/values/strings.xml
create mode 100644 SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/OemCommands.java
create mode 100644 SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/SamsungServiceModeActivity.java
create mode 100644 SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/SecretBroadcastReceiver.java
diff --git a/SamsungServiceMode/Android.mk b/SamsungServiceMode/Android.mk
new file mode 100644
index 0000000..4437eb3
--- /dev/null
+++ b/SamsungServiceMode/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := SamsungServiceMode
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+# Build the test package
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/SamsungServiceMode/AndroidManifest.xml b/SamsungServiceMode/AndroidManifest.xml
new file mode 100644
index 0000000..f46af4d
--- /dev/null
+++ b/SamsungServiceMode/AndroidManifest.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SamsungServiceMode/res/layout/list_item.xml b/SamsungServiceMode/res/layout/list_item.xml
new file mode 100644
index 0000000..4f45ed4
--- /dev/null
+++ b/SamsungServiceMode/res/layout/list_item.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/SamsungServiceMode/res/layout/main.xml b/SamsungServiceMode/res/layout/main.xml
new file mode 100644
index 0000000..6b8cba6
--- /dev/null
+++ b/SamsungServiceMode/res/layout/main.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/SamsungServiceMode/res/values/strings.xml b/SamsungServiceMode/res/values/strings.xml
new file mode 100644
index 0000000..d033037
--- /dev/null
+++ b/SamsungServiceMode/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+ Hello World, SamsungServiceModeActivity!
+ Service Mode
+ Send
+
diff --git a/SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/OemCommands.java b/SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/OemCommands.java
new file mode 100644
index 0000000..ccc2d48
--- /dev/null
+++ b/SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/OemCommands.java
@@ -0,0 +1,110 @@
+package com.cyanogenmod.samsungservicemode;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import android.util.Log;
+
+class OemCommands {
+
+ private static final String TAG = "OemCommands";
+
+ public static final char OEM_SERVM_FUNCTAG = 1;
+ public static final char OEM_SM_ACTION = 0;
+ public static final char OEM_SM_QUERY = 1;
+ public static final char OEM_SM_DUMMY = 0;
+ public static final char OEM_SM_ENTER_MODE_MESSAGE = 1;
+ public static final char OEM_SM_END_MODE_MESSAGE = 2;
+ public static final char OEM_SM_PROCESS_KEY_MESSAGE = 3;
+ public static final char OEM_SM_GET_DISPLAY_DATA_MESSAGE = 4;
+ public static final char OEM_SM_TYPE_TEST_MANUAL = 1;
+ public static final char OEM_SM_TYPE_TEST_AUTO = 2;
+ public static final char OEM_SM_TYPE_NAM_EDIT = 3;
+ public static final char OEM_SM_TYPE_MONITOR = 4;
+ public static final char OEM_SM_TYPE_PHONE_TEST = 5;
+ public static final char OEM_SM_TYPE_SUB_ENTER = 0;
+ public static final char OEM_SM_TYPE_SUB_SW_VERSION_ENTER = 1;
+ public static final char OEM_SM_TYPE_SUB_FTA_SW_VERSION_ENTER = 2;
+ public static final char OEM_SM_TYPE_SUB_FTA_HW_VERSION_ENTER = 3;
+ public static final char OEM_SM_TYPE_SUB_ALL_VERSION_ENTER = 4;
+ public static final char OEM_SM_TYPE_SUB_BATTERY_INFO_ENTER = 5;
+ public static final char OEM_SM_TYPE_SUB_CIPHERING_PROTECTION_ENTER = 6;
+ public static final char OEM_SM_TYPE_SUB_INTEGRITY_PROTECTION_ENTER = 7;
+ public static final char OEM_SM_TYPE_SUB_IMEI_READ_ENTER = 8;
+ public static final char OEM_SM_TYPE_SUB_BLUETOOTH_TEST_ENTER = 9;
+ public static final char OEM_SM_TYPE_SUB_VIBRATOR_TEST_ENTER = 10;
+ public static final char OEM_SM_TYPE_SUB_MELODY_TEST_ENTER = 11;
+ public static final char OEM_SM_TYPE_SUB_MP3_TEST_ENTER = 12;
+ public static final char OEM_SM_TYPE_SUB_FACTORY_RESET_ENTER = 13;
+ public static final char OEM_SM_TYPE_SUB_FACTORY_PRECONFIG_ENTER = 14;
+ public static final char OEM_SM_TYPE_SUB_TFS4_EXPLORE_ENTER = 15;
+ public static final char OEM_SM_TYPE_SUB_RSC_FILE_VERSION_ENTER = 17;
+ public static final char OEM_SM_TYPE_SUB_USB_DRIVER_ENTER = 18;
+ public static final char OEM_SM_TYPE_SUB_USB_UART_DIAG_CONTROL_ENTER = 19;
+ public static final char OEM_SM_TYPE_SUB_RRC_VERSION_ENTER = 20;
+ public static final char OEM_SM_TYPE_SUB_GPSONE_SS_TEST_ENTER = 21;
+ public static final char OEM_SM_TYPE_SUB_BAND_SEL_ENTER = 22;
+ public static final char OEM_SM_TYPE_SUB_GCF_TESTMODE_ENTER = 23;
+ public static final char OEM_SM_TYPE_SUB_GSM_FACTORY_AUDIO_LB_ENTER = 24;
+ public static final char OEM_SM_TYPE_SUB_FACTORY_VF_TEST_ENTER = 25;
+ public static final char OEM_SM_TYPE_SUB_TOTAL_CALL_TIME_INFO_ENTER = 26;
+ public static final char OEM_SM_TYPE_SUB_SELLOUT_SMS_ENABLE_ENTER = 27;
+ public static final char OEM_SM_TYPE_SUB_SELLOUT_SMS_DISABLE_ENTER = 28;
+ public static final char OEM_SM_TYPE_SUB_SELLOUT_SMS_TEST_MODE_ON = 29;
+ public static final char OEM_SM_TYPE_SUB_SELLOUT_SMS_PRODUCT_MODE_ON = 30;
+ public static final char OEM_SM_TYPE_SUB_GET_SELLOUT_SMS_INFO_ENTER = 31;
+ public static final char OEM_SM_TYPE_SUB_TST_AUTO_ANSWER_ENTER = 32;
+ public static final char OEM_SM_TYPE_SUB_TST_NV_RESET_ENTER = 33;
+ public static final char OEM_SM_TYPE_SUB_TST_FTA_SW_VERSION_ENTER = 4098;
+ public static final char OEM_SM_TYPE_SUB_TST_FTA_HW_VERSION_ENTER = 4099;
+
+ public static byte[] getEnterServiceModeData(int modeType, int subType, int query) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeByte(OEM_SERVM_FUNCTAG);
+ dos.writeByte(OEM_SM_ENTER_MODE_MESSAGE);
+ dos.writeShort(7);
+ dos.writeByte(modeType);
+ dos.writeByte(subType);
+ dos.writeByte(query);
+ return baos.toByteArray();
+ } catch (IOException e) {
+ Log.e(TAG, "", e);
+ }
+ return null;
+ }
+
+ public static byte[] getEndServiceModeData(int modeType) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeByte(OEM_SERVM_FUNCTAG);
+ dos.writeByte(OEM_SM_END_MODE_MESSAGE);
+ dos.writeShort(5);
+ dos.writeByte(modeType);
+ return baos.toByteArray();
+ } catch (IOException e) {
+ Log.e(TAG, "", e);
+ }
+ return null;
+ }
+
+ public static byte[] getPressKeyData(int keycode, int query) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeByte(OEM_SERVM_FUNCTAG);
+ dos.writeByte(OEM_SM_PROCESS_KEY_MESSAGE);
+ dos.writeShort(6);
+ dos.writeByte(keycode);
+ dos.writeByte(query);
+ return baos.toByteArray();
+ } catch (IOException e) {
+ Log.e(TAG, "", e);
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/SamsungServiceModeActivity.java b/SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/SamsungServiceModeActivity.java
new file mode 100644
index 0000000..59c0139
--- /dev/null
+++ b/SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/SamsungServiceModeActivity.java
@@ -0,0 +1,277 @@
+package com.cyanogenmod.samsungservicemode;
+
+import android.app.Activity;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+public class SamsungServiceModeActivity extends Activity implements AdapterView.OnItemClickListener {
+
+ private static final String TAG = "SamsungServiceModeActivity";
+
+ public static final String EXTRA_SECRET_CODE = "secret_code";
+
+ private static final int ID_SERVICE_MODE_REFRESH = 1001;
+ private static final int ID_SERVICE_MODE_REQUEST = 1008;
+ private static final int ID_SERVICE_MODE_END = 1009;
+
+ private static final int CHARS_PER_LINE = 34;
+ private static final int LINES = 11;
+
+ private ListView mListView;
+ private String[] mDisplay = new String[LINES];
+
+ private int mCurrentSvcMode;
+ private int mCurrentModeType;
+
+ // Disable back when initialized with certain commands due to crash
+ private boolean mAllowBack;
+ private boolean mFirstRun = true;
+ private String mFirstPageHead;
+
+ private Phone mPhone;
+ private Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case ID_SERVICE_MODE_REFRESH:
+ Log.v(TAG, "Tick");
+ byte[] data = null;
+ switch(mCurrentSvcMode) {
+ case OemCommands.OEM_SM_ENTER_MODE_MESSAGE:
+ data = OemCommands.getEnterServiceModeData(0, 0, OemCommands.OEM_SM_QUERY);
+ break;
+ case OemCommands.OEM_SM_PROCESS_KEY_MESSAGE:
+ data = OemCommands.getPressKeyData('\0', OemCommands.OEM_SM_QUERY);
+ break;
+ default:
+ Log.e(TAG, "Unknown mode: " + mCurrentSvcMode);
+ break;
+ }
+
+ if (data != null) {
+ sendRequest(data, ID_SERVICE_MODE_REQUEST);
+ }
+ break;
+ case ID_SERVICE_MODE_REQUEST:
+ AsyncResult result = (AsyncResult)msg.obj;
+ if (result.exception != null) {
+ Log.e(TAG, "", result.exception);
+ return;
+ }
+ if (result.result == null) {
+ Log.v(TAG, "No need to refresh.");
+ return;
+ }
+ byte[] aob = (byte[])result.result;
+
+ if (aob.length == 0) {
+ Log.v(TAG, "Length = 0");
+ return;
+ }
+
+ int lines = aob.length / CHARS_PER_LINE;
+ if (lines > LINES) {
+ Log.e(TAG, "Datasize " + aob.length + " larger than expected");
+ return;
+ }
+
+ for (int i = 0; i < lines; i++) {
+ StringBuilder strb = new StringBuilder(CHARS_PER_LINE);
+ for (int j = 2; i < CHARS_PER_LINE; j++) {
+ int pos = i * CHARS_PER_LINE + j;
+ if (pos >= aob.length) {
+ Log.e(TAG, "Unexpected EOF");
+ break;
+ }
+ if (aob[pos] == 0) {
+ break;
+ }
+ strb.append((char)aob[pos]);
+ }
+ mDisplay[i] = strb.toString();
+ }
+
+ mListView.setAdapter(new ArrayAdapter(
+ SamsungServiceModeActivity.this, R.layout.list_item, mDisplay));
+
+ if (mFirstRun) {
+ mFirstPageHead = mDisplay[0];
+ mFirstRun = false;
+ }
+
+ if (mDisplay[0].contains("End service mode")) {
+ finish();
+ } else if (((mDisplay[0].contains("[")) && (mDisplay[0].contains("]")))
+ || ((mDisplay[1].contains("[")) && (mDisplay[1].contains("]")))) {
+ // This is a menu, don't refresh
+ } else if ((mDisplay[0].length() != 0) && (mDisplay[1].length() == 0)
+ && (mDisplay[0].charAt(1) > 48) && (mDisplay[0].charAt(1) < 58)) {
+ // Only numerical display, refresh
+ mHandler.sendEmptyMessageDelayed(ID_SERVICE_MODE_REFRESH, 200);
+ } else {
+ // Periodical refresh
+ mHandler.sendEmptyMessageDelayed(ID_SERVICE_MODE_REFRESH, 1500);
+ }
+ break;
+ case ID_SERVICE_MODE_END:
+ Log.v(TAG, "Service Mode End");
+ break;
+ }
+ }
+
+ };
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ mListView = (ListView)findViewById(R.id.displayList);
+ mListView.setOnItemClickListener(this);
+
+ mPhone = PhoneFactory.getDefaultPhone();
+
+ // Go to the page specified by the code used to enter service mode
+ String code = getIntent().getStringExtra(EXTRA_SECRET_CODE);
+
+ // Default to main page
+ int modeType = OemCommands.OEM_SM_TYPE_TEST_MANUAL;
+ int subType = OemCommands.OEM_SM_TYPE_SUB_ENTER;
+ mAllowBack = true; // Some commands don't like having "back" executed on them
+
+ if (TextUtils.isEmpty(code) || code.equals("197328640")) {
+ // Use default (this exists to prevent NPE when code is null)
+ }
+ else if (code.equals("0011")) {
+ modeType = OemCommands.OEM_SM_TYPE_MONITOR;
+ subType = OemCommands.OEM_SM_TYPE_SUB_ENTER;
+ } else if (code.equals("0228")) { // 0BAT
+ subType = OemCommands.OEM_SM_TYPE_SUB_BATTERY_INFO_ENTER;
+ } else if (code.equals("32489")) {
+ subType = OemCommands.OEM_SM_TYPE_SUB_CIPHERING_PROTECTION_ENTER;
+ } else if (code.equals("2580")) { // ALT0
+ subType = OemCommands.OEM_SM_TYPE_SUB_INTEGRITY_PROTECTION_ENTER;
+ } else if (code.equals("9090") || code.equals("7284")) { // PATH
+ subType = OemCommands.OEM_SM_TYPE_SUB_USB_UART_DIAG_CONTROL_ENTER;
+ mAllowBack = false;
+ } else if (code.equals("0599") || code.equals("301279") || code.equals("279301")) {
+ subType = OemCommands.OEM_SM_TYPE_SUB_RRC_VERSION_ENTER;
+ mAllowBack = false;
+ } else if (code.equals("2263")) { // BAND
+ subType = OemCommands.OEM_SM_TYPE_SUB_BAND_SEL_ENTER;
+ mAllowBack = false;
+ } else if (code.equals("4238378")) { // GCFTEST
+ subType = OemCommands.OEM_SM_TYPE_SUB_GCF_TESTMODE_ENTER;
+ mAllowBack = false;
+ } else if (code.equals("0283")) { // 0AUD
+ subType = OemCommands.OEM_SM_TYPE_SUB_GSM_FACTORY_AUDIO_LB_ENTER;
+ } else if (code.equals("1575")) {
+ subType = OemCommands.OEM_SM_TYPE_SUB_GPSONE_SS_TEST_ENTER;
+ } else if (code.equals("73876766")) { // SETSMSON
+ subType = OemCommands.OEM_SM_TYPE_SUB_SELLOUT_SMS_ENABLE_ENTER;
+ mAllowBack = false;
+ } else if (code.equals("738767633")) { // SETSMSOFF
+ subType = OemCommands.OEM_SM_TYPE_SUB_SELLOUT_SMS_DISABLE_ENTER;
+ mAllowBack = false;
+ } else if (code.equals("7387678378")) { // SETSMSTEST
+ subType = OemCommands.OEM_SM_TYPE_SUB_SELLOUT_SMS_TEST_MODE_ON;
+ mAllowBack = false;
+ } else if (code.equals("7387677763")) { // SETSMSPROD
+ subType = OemCommands.OEM_SM_TYPE_SUB_SELLOUT_SMS_PRODUCT_MODE_ON;
+ mAllowBack = false;
+ } else if (code.equals("4387264636")) {
+ subType = OemCommands.OEM_SM_TYPE_SUB_GET_SELLOUT_SMS_INFO_ENTER;
+ mAllowBack = false;
+ } else if (code.equals("6984125*") || code.equals("2886")) { // AUTO
+ // crash
+ subType = OemCommands.OEM_SM_TYPE_SUB_TST_AUTO_ANSWER_ENTER;
+ } else if (code.equals("2767*2878")) {
+ // crash
+ subType = OemCommands.OEM_SM_TYPE_SUB_TST_NV_RESET_ENTER;
+ } else if (code.equals("1111")) {
+ subType = OemCommands.OEM_SM_TYPE_SUB_TST_FTA_SW_VERSION_ENTER;
+ mAllowBack = false;
+ } else if (code.equals("2222")) {
+ subType = OemCommands.OEM_SM_TYPE_SUB_TST_FTA_HW_VERSION_ENTER;
+ mAllowBack = false;
+ }
+
+ enterServiceMode(modeType, subType);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (!mAllowBack && mDisplay[0].equals(mFirstPageHead)) {
+ Log.v(TAG, "Back disabled. Ending service mode.");
+ endServiceMode();
+ } else {
+ sendChar((char) 92);
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ String str = mDisplay[position];
+ int start = str.indexOf('[');
+ int end = str.indexOf(']');
+
+ if (start == -1 || end == -1) {
+ // This menu is not clickable
+ return;
+ }
+ sendChar(str.charAt(start + 1));
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mHandler.removeMessages(ID_SERVICE_MODE_REFRESH);
+ }
+
+ private void enterServiceMode(int modeType, int subType) {
+ mCurrentSvcMode = OemCommands.OEM_SM_ENTER_MODE_MESSAGE;
+ mCurrentModeType = modeType;
+ byte[] data = OemCommands.getEnterServiceModeData(modeType, subType, OemCommands.OEM_SM_ACTION);
+ sendRequest(data, ID_SERVICE_MODE_REQUEST);
+ }
+
+ private void endServiceMode() {
+ mCurrentSvcMode = OemCommands.OEM_SM_END_MODE_MESSAGE;
+ mHandler.removeMessages(ID_SERVICE_MODE_REFRESH);
+ byte[] data = OemCommands.getEndServiceModeData(mCurrentModeType);
+ sendRequest(data, ID_SERVICE_MODE_END);
+ finish();
+ }
+
+ private void sendChar(char chr) {
+ mCurrentSvcMode = OemCommands.OEM_SM_PROCESS_KEY_MESSAGE;
+ mHandler.removeMessages(ID_SERVICE_MODE_REFRESH);
+ if (chr >= 'a' && chr <= 'f') {
+ chr = Character.toUpperCase(chr);
+ } else if (chr == '-') {
+ chr = '*';
+ }
+
+ byte[] data = OemCommands.getPressKeyData(chr, OemCommands.OEM_SM_ACTION);
+ sendRequest(data, ID_SERVICE_MODE_REQUEST);
+ }
+
+ private void sendRequest(byte[] data, int id) {
+ Message msg = mHandler.obtainMessage(id);
+ mPhone.invokeOemRilRequestRaw(data, msg);
+ }
+
+}
\ No newline at end of file
diff --git a/SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/SecretBroadcastReceiver.java b/SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/SecretBroadcastReceiver.java
new file mode 100644
index 0000000..b3366f6
--- /dev/null
+++ b/SamsungServiceMode/src/com/cyanogenmod/samsungservicemode/SecretBroadcastReceiver.java
@@ -0,0 +1,19 @@
+package com.cyanogenmod.samsungservicemode;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class SecretBroadcastReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String code = intent.getData().getHost();
+ Intent i = new Intent(Intent.ACTION_MAIN);
+ i.setClass(context, SamsungServiceModeActivity.class);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ i.putExtra(SamsungServiceModeActivity.EXTRA_SECRET_CODE, code);
+ context.startActivity(i);
+ }
+
+}
--
cgit v1.1