From 6d8719ab356a3ecbd0b526a9ded0cabb17ab2021 Mon Sep 17 00:00:00 2001 From: xLaMbChOpSx <ryanskungfu@me.com> Date: Tue, 6 May 2014 22:33:43 +1000 Subject: [PATCH 1/2] Neighbouring Cell Info, Ciphering Indicator, Samsung MultiRil Implementation Full Credits to Alexey Illarionov for his implementation of the Replicant libsecril-client-sap methods to enable access to the OEM_HOOK_RAW methods through the MultiClient interface, please visit Alexey's Github here: https://github.com/illarionov/SamsungRilMulticlient and star his work without him this would not be possible. Advanced Cell Fragment added to display the Neighbouring Cell information in two ways firstly through telephony manager methods which does not work on Samsung Devices, a fallback is available through the methods developed by Alexey and will display if these are successful. Ciphering Indicator also uses Alexey's methods and will display on Samsung devices. Further manipulation of the results provided will enable an increase in the functionality of attempting to detect IMSI activity and will be developed over time. Slight change to the preference to regarding the persistent service operation changing the preference to ENABLE persistent operation which is defaulted to ON. --- app/build.gradle | 4 + .../java/com/SecUpwN/AIMSICD/AIMSICD.java | 22 +- .../com/SecUpwN/AIMSICD/CellInfoFragment.java | 209 ++++++++++ .../java/com/SecUpwN/AIMSICD/Helpers.java | 40 ++ .../java/com/SecUpwN/AIMSICD/OemCommands.java | 47 +-- .../AIMSICD/rilexecutor/DetectResult.java | 34 ++ .../SecUpwN/AIMSICD/rilexecutor/HexDump.java | 163 ++++++++ .../AIMSICD/rilexecutor/OemRilExecutor.java | 29 ++ .../AIMSICD/rilexecutor/RawResult.java | 13 + .../SamsungMulticlientRilExecutor.java | 377 +++++++++++++++++ .../AIMSICD/service/AimsicdService.java | 381 ++++++++++++++++-- app/src/main/res/layout/cell_fragment.xml | 96 +++++ app/src/main/res/layout/db_view.xml | 14 +- app/src/main/res/values/strings.xml | 9 +- app/src/main/res/xml/preferences.xml | 2 +- 15 files changed, 1355 insertions(+), 85 deletions(-) create mode 100644 app/src/main/java/com/SecUpwN/AIMSICD/CellInfoFragment.java create mode 100644 app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/DetectResult.java create mode 100644 app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/HexDump.java create mode 100644 app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/OemRilExecutor.java create mode 100644 app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/RawResult.java create mode 100644 app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/SamsungMulticlientRilExecutor.java create mode 100644 app/src/main/res/layout/cell_fragment.xml diff --git a/app/build.gradle b/app/build.gradle index cfa139d7..63af65fe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,6 +24,10 @@ android { } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } } diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/AIMSICD.java b/app/src/main/java/com/SecUpwN/AIMSICD/AIMSICD.java index a25e41c6..6e2078a2 100644 --- a/app/src/main/java/com/SecUpwN/AIMSICD/AIMSICD.java +++ b/app/src/main/java/com/SecUpwN/AIMSICD/AIMSICD.java @@ -205,7 +205,7 @@ public class AIMSICD extends FragmentActivity { MenuItem mTrackLocation = menu.findItem(R.id.track_location); MenuItem mTrackFemtocell = menu.findItem(R.id.track_femtocell); - if (mAimsicdService.TrackingCell) { + if (mBound && mAimsicdService.isTrackingCell()) { mTrackCell.setTitle(R.string.untrack_cell); mTrackCell.setIcon(R.drawable.track_cell); } else { @@ -213,7 +213,7 @@ public class AIMSICD extends FragmentActivity { mTrackCell.setIcon(R.drawable.untrack_cell); } - if (mAimsicdService.TrackingLocation) { + if (mBound && mAimsicdService.isTrackingLocation()) { mTrackLocation.setTitle(R.string.untrack_location); mTrackLocation.setIcon(R.drawable.ic_action_location_found); } else { @@ -221,8 +221,8 @@ public class AIMSICD extends FragmentActivity { mTrackLocation.setIcon(R.drawable.ic_action_location_off); } - if (mAimsicdService.getPhoneID() == TelephonyManager.PHONE_TYPE_CDMA) { - if (mAimsicdService.TrackingFemtocell) { + if (mBound && mAimsicdService.getPhoneID() == TelephonyManager.PHONE_TYPE_CDMA) { + if (mBound && mAimsicdService.isTrackingFemtocell()) { mTrackFemtocell.setTitle(R.string.untrack_femtocell); mTrackFemtocell.setIcon(R.drawable.ic_action_network_cell); } else { @@ -265,7 +265,11 @@ public class AIMSICD extends FragmentActivity { return true; case R.id.update_opencelldata: double[] loc = mAimsicdService.getLastLocation(); - getOpenCellData(loc[0], loc[1]); + if (loc[0] != 0.0 && loc[1] != 0.0) { + getOpenCellData(loc[0], loc[1]); + } else { + Helpers.sendMsg(mContext, "Unable to determine your last location, enable Location Services (GPS) and try again."); + } return true; case R.id.app_exit: finish(); @@ -304,7 +308,7 @@ public class AIMSICD extends FragmentActivity { * Cell Information Tracking - Enable/Disable */ private void trackcell() { - if (mAimsicdService.TrackingCell) { + if (mAimsicdService.isTrackingCell()) { mAimsicdService.setCellTracking(false); } else { mAimsicdService.setCellTracking(true); @@ -315,7 +319,7 @@ public class AIMSICD extends FragmentActivity { * Location Information Tracking - Enable/Disable */ private void tracklocation() { - if (mAimsicdService.TrackingLocation) { + if (mAimsicdService.isTrackingLocation()) { mAimsicdService.setLocationTracking(false); } else { mAimsicdService.setLocationTracking(true); @@ -326,7 +330,7 @@ public class AIMSICD extends FragmentActivity { * FemtoCell Detection (CDMA Phones ONLY) - Enable/Disable */ private void trackFemtocell() { - if (mAimsicdService.TrackingFemtocell) { + if (mAimsicdService.isTrackingFemtocell()) { mAimsicdService.stopTrackingFemto(); } else { mAimsicdService.startTrackingFemto(); @@ -512,6 +516,8 @@ public class AIMSICD extends FragmentActivity { titles.add(getString(R.string.device_info)); fragments.add(new DbViewerFragment(mContext)); titles.add(getString(R.string.db_viewer)); + fragments.add(new CellInfoFragment(mContext)); + titles.add(getString(R.string.cell_info_title)); } @Override public Fragment getItem(int position) { diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/CellInfoFragment.java b/app/src/main/java/com/SecUpwN/AIMSICD/CellInfoFragment.java new file mode 100644 index 00000000..0f41385c --- /dev/null +++ b/app/src/main/java/com/SecUpwN/AIMSICD/CellInfoFragment.java @@ -0,0 +1,209 @@ +package com.SecUpwN.AIMSICD; + +import com.SecUpwN.AIMSICD.rilexecutor.DetectResult; +import com.SecUpwN.AIMSICD.service.AimsicdService; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v4.app.Fragment; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CellInfoFragment extends Fragment { + private AimsicdService mAimsicdService; + private View mView; + private TextView mNeighbouringCells; + private TextView mNeighbouringTotal; + private TextView mNeighbouringTotalLabel; + private TextView mCipheringIndicatorLabel; + private TextView mCipheringIndicator; + + private boolean mBound; + private Context mContext; + + private Map<Integer,Integer> mNeighborMapUMTS = new HashMap<Integer,Integer>(); + private Map<String,Integer> mNeighborMapGSM = new HashMap<String,Integer>(); + + public CellInfoFragment (Context context) { + mContext = context; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mView= inflater.inflate(R.layout.cell_fragment, + container, false); + mNeighbouringCells = (TextView) mView.findViewById(R.id.neighbouring_cells); + mNeighbouringTotal = (TextView) mView.findViewById(R.id.neighbouring_number); + mNeighbouringTotalLabel = (TextView) mView.findViewById(R.id.neighbouring_number_label); + mCipheringIndicatorLabel = (TextView) mView.findViewById(R.id.ciphering_indicator_title); + mCipheringIndicator = (TextView) mView.findViewById(R.id.ciphering_indicator); + Button refresh = (Button) mView.findViewById(R.id.button_refresh); + refresh.setOnClickListener(new btnClick()); + + return mView; + } + + @Override + public void onDestroy() { + super.onDestroy(); + // Unbind from the service + if (mBound) { + mContext.unbindService(mConnection); + mBound = false; + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Bind to LocalService + Intent intent = new Intent(mContext, AimsicdService.class); + //Start Service before binding to keep it resident when activity is destroyed + mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + /** + * Service Connection to bind the activity to the service + */ + private final ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + mAimsicdService = ((AimsicdService.AimscidBinder) service).getService(); + mBound = true; + updateUI(); + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mBound = false; + } + }; + + private class btnClick implements View.OnClickListener { + @Override + public void onClick(View v) { + updateUI(); + } + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if (isVisibleToUser) { + updateUI(); + } + } + + private void updateUI() { + if (mBound) { + mAimsicdService.updateNeighbouringCells(); + mNeighborMapUMTS = mAimsicdService.getUMTSNeighbouringCells(); + mNeighborMapGSM = mAimsicdService.getGSMNeighbouringCells(); + + mNeighbouringTotal + .setText(String.valueOf(mAimsicdService.getNeighbouringCellSize()) + "/" + + String.valueOf(mNeighborMapUMTS.size() + mNeighborMapGSM.size())); + + StringBuilder sb = new StringBuilder(); + int i = 1; + if (!mNeighborMapUMTS.isEmpty()) + for (Object key : mNeighborMapUMTS.keySet()) { + sb.append(i) + .append(") PSC: ") + .append(key) + .append(" RSCP: ") + .append(mNeighborMapUMTS.get(key)) //TS25.133 section 9.1.1.3 + .append(" dBm"); + if (i < mNeighborMapUMTS.size() + mNeighborMapGSM.size()) + sb.append("\n"); + i++; + } + if (!mNeighborMapGSM.isEmpty()) + for (Object key : mNeighborMapGSM.keySet()) { + sb.append(i) + .append(") LAC-CID: ") + .append(key) + .append(" RSSI: ") + .append(mNeighborMapGSM.get(key)) //TS27.007 section 8.5 + .append(" dBm"); + if (i < mNeighborMapUMTS.size() + mNeighborMapGSM.size()) + sb.append("\n"); + i++; + } + mNeighbouringCells.setText(sb); + + //Try SamSung MultiRil Implementation + if (mNeighborMapGSM.isEmpty() && mNeighborMapUMTS.isEmpty()) { + DetectResult rilStatus = mAimsicdService.getRilExecutorStatus(); + if (rilStatus.available) { + //new RequestOemInfoTask().execute(); + new RequestOemInfoTask().execute(); + } + } else { + mNeighbouringTotal.setVisibility(View.VISIBLE); + mNeighbouringTotalLabel.setVisibility(View.VISIBLE); + } + } + } + + void updateCipheringIndicator() { + final List<String> list = mAimsicdService.getCipheringInfo(); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + if (list != null) { + mCipheringIndicatorLabel.setVisibility(View.VISIBLE); + mCipheringIndicator.setVisibility(View.VISIBLE); + mCipheringIndicator.setText(TextUtils.join("\n", list)); + + } + } + }); + } + + void updateNeighbouringCells() { + final List<String> list = mAimsicdService.getNeighbours(); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + if (list != null) { + mNeighbouringCells.setText(TextUtils.join("\n", list)); + mNeighbouringTotal.setVisibility(View.GONE); + mNeighbouringTotalLabel.setVisibility(View.GONE); + } + } + }); + } + + private class RequestOemInfoTask extends AsyncTask<Void, Void, Void> { + @Override + protected Void doInBackground(Void... string) { + if (!mBound) return null; + updateNeighbouringCells(); + updateCipheringIndicator(); + + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + } + } + +} diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/Helpers.java b/app/src/main/java/com/SecUpwN/AIMSICD/Helpers.java index 43e3ea2e..5499f571 100644 --- a/app/src/main/java/com/SecUpwN/AIMSICD/Helpers.java +++ b/app/src/main/java/com/SecUpwN/AIMSICD/Helpers.java @@ -25,17 +25,21 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Environment; +import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.List; public class Helpers { private static final String TAG = "AIMSICD_Helpers"; + private static final int CHARS_PER_LINE = 34; /** * Long toast message @@ -198,5 +202,41 @@ public class Helpers { } return mExternalStorageAvailable; } + + public static List<String> unpackListOfStrings(byte aob[]) { + + if (aob.length == 0) { + Log.v(TAG, "Length = 0"); + return Collections.emptyList(); + } + + int lines = aob.length / CHARS_PER_LINE; + + String[] display = new String[lines]; + for (int i = 0; i < lines; i++) { + int offset, byteCount; + offset = i * CHARS_PER_LINE + 2; + byteCount = 0; + + if (offset + byteCount >= aob.length) { + Log.e(TAG, "Unexpected EOF"); + break; + } + + while (aob[offset + byteCount] != 0 && (byteCount < CHARS_PER_LINE)) { + byteCount += 1; + if (offset + byteCount >= aob.length) { + Log.e(TAG, "Unexpected EOF"); + break; + } + } + display[i] = new String(aob, offset, byteCount).trim(); + } + + int newLength = display.length; + while (newLength > 0 && TextUtils.isEmpty(display[newLength - 1])) newLength -= 1; + + return Arrays.asList(Arrays.copyOf(display, newLength)); + } } diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/OemCommands.java b/app/src/main/java/com/SecUpwN/AIMSICD/OemCommands.java index fb8ea086..076a77c4 100644 --- a/app/src/main/java/com/SecUpwN/AIMSICD/OemCommands.java +++ b/app/src/main/java/com/SecUpwN/AIMSICD/OemCommands.java @@ -17,14 +17,13 @@ package com.SecUpwN.AIMSICD; -import android.content.Context; import android.util.Log; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; -class OemCommands { +public class OemCommands { private static final String TAG = "AIMSICD_OemCommands"; @@ -77,31 +76,13 @@ class OemCommands { 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; - private int mApiVersion; - - private OemCommands(int apiVersion) { - mApiVersion = apiVersion; - } - - public static OemCommands getInstance(Context context) { - int apiVersion = context.getResources().getInteger(R.integer.config_api_version); - return new OemCommands(apiVersion); - } - - public byte[] getEnterServiceModeData(int modeType, int subType, int query) { + 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); - if (mApiVersion == 1) { - dos.writeShort(7); - } else if (mApiVersion == 2) { - dos.writeShort(8); - dos.writeByte(4); - } else { - throw new IllegalArgumentException("Invalid API version " + mApiVersion); - } + dos.writeShort(7); dos.writeByte(modeType); dos.writeByte(subType); dos.writeByte(query); @@ -112,20 +93,13 @@ class OemCommands { return null; } - public byte[] getEndServiceModeData(int modeType) { + 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); - if (mApiVersion == 1) { - dos.writeShort(5); - } else if (mApiVersion == 2) { - dos.writeShort(6); - dos.writeByte(4); - } else { - throw new IllegalArgumentException("Invalid API version " + mApiVersion); - } + dos.writeShort(5); dos.writeByte(modeType); return baos.toByteArray(); } catch (IOException e) { @@ -134,20 +108,13 @@ class OemCommands { return null; } - public byte[] getPressKeyData(int keycode, int query) { + 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); - if (mApiVersion == 1) { - dos.writeShort(6); - } else if (mApiVersion == 2) { - dos.writeShort(7); - dos.writeByte(4); - } else { - throw new IllegalArgumentException("Invalid API version " + mApiVersion); - } + dos.writeShort(6); dos.writeByte(keycode); dos.writeByte(query); return baos.toByteArray(); diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/DetectResult.java b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/DetectResult.java new file mode 100644 index 00000000..dc40b82d --- /dev/null +++ b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/DetectResult.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 Alexey Illarionov + * + * 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.SecUpwN.AIMSICD.rilexecutor; + +public class DetectResult { + + public final boolean available; + + public final String error; + + static final DetectResult AVAILABLE = new DetectResult(true, null); + + private DetectResult(boolean available, String error) { + this.available = available; + this.error = error; + } + + static DetectResult Unavailable(String error) { + return new DetectResult(false, error); + } +} diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/HexDump.java b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/HexDump.java new file mode 100644 index 00000000..5a1929ab --- /dev/null +++ b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/HexDump.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 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.SecUpwN.AIMSICD.rilexecutor; + +public class HexDump +{ + private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + public static String dumpHexString(byte[] array) + { + return dumpHexString(array, 0, array.length); + } + + public static String dumpHexString(byte[] array, int offset, int length) + { + StringBuilder result = new StringBuilder(); + + byte[] line = new byte[16]; + int lineIndex = 0; + + result.append("\n0x"); + result.append(toHexString(offset)); + + for (int i = offset ; i < offset + length ; i++) + { + if (lineIndex == 16) + { + result.append(" "); + + for (int j = 0 ; j < 16 ; j++) + { + if (line[j] > ' ' && line[j] < '~') + { + result.append(new String(line, j, 1)); + } + else + { + result.append("."); + } + } + + result.append("\n0x"); + result.append(toHexString(i)); + lineIndex = 0; + } + + byte b = array[i]; + result.append(" "); + result.append(HEX_DIGITS[(b >>> 4) & 0x0F]); + result.append(HEX_DIGITS[b & 0x0F]); + + line[lineIndex++] = b; + } + + if (lineIndex != 16) + { + int count = (16 - lineIndex) * 3; + count++; + for (int i = 0 ; i < count ; i++) + { + result.append(" "); + } + + for (int i = 0 ; i < lineIndex ; i++) + { + if (line[i] > ' ' && line[i] < '~') + { + result.append(new String(line, i, 1)); + } + else + { + result.append("."); + } + } + } + + return result.toString(); + } + + public static String toHexString(byte b) + { + return toHexString(toByteArray(b)); + } + + public static String toHexString(byte[] array) + { + return toHexString(array, 0, array.length); + } + + public static String toHexString(byte[] array, int offset, int length) + { + char[] buf = new char[length * 2]; + + int bufIndex = 0; + for (int i = offset ; i < offset + length; i++) + { + byte b = array[i]; + buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F]; + buf[bufIndex++] = HEX_DIGITS[b & 0x0F]; + } + + return new String(buf); + } + + public static String toHexString(int i) + { + return toHexString(toByteArray(i)); + } + + public static byte[] toByteArray(byte b) + { + byte[] array = new byte[1]; + array[0] = b; + return array; + } + + public static byte[] toByteArray(int i) + { + byte[] array = new byte[4]; + + array[3] = (byte)(i & 0xFF); + array[2] = (byte)((i >> 8) & 0xFF); + array[1] = (byte)((i >> 16) & 0xFF); + array[0] = (byte)((i >> 24) & 0xFF); + + return array; + } + + private static int toByte(char c) + { + if (c >= '0' && c <= '9') return (c - '0'); + if (c >= 'A' && c <= 'F') return (c - 'A' + 10); + if (c >= 'a' && c <= 'f') return (c - 'a' + 10); + + throw new RuntimeException ("Invalid hex char '" + c + "'"); + } + + public static byte[] hexStringToByteArray(String hexString) + { + int length = hexString.length(); + byte[] buffer = new byte[length / 2]; + + for (int i = 0 ; i < length ; i += 2) + { + buffer[i / 2] = (byte)((toByte(hexString.charAt(i)) << 4) | toByte(hexString.charAt(i+1))); + } + + return buffer; + } +} diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/OemRilExecutor.java b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/OemRilExecutor.java new file mode 100644 index 00000000..ba3f866a --- /dev/null +++ b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/OemRilExecutor.java @@ -0,0 +1,29 @@ +package com.SecUpwN.AIMSICD.rilexecutor; + +import android.os.Message; + +public interface OemRilExecutor { + + public DetectResult detect(); + + public void start(); + + public void stop(); + + /** + * Invokes RIL_REQUEST_OEM_HOOK_RAW. + * + * @param data The data for the request. + * @param response <strong>On success</strong>, + * (byte[])(((AsyncResult)response.obj).result) + * <strong>On failure</strong>, + * (((RawResult)response.obj).result) == null and + * (((RawResult)response.obj).exception) being an instance of + * com.android.internal.telephony.gsm.CommandException + * + * @see #invokeOemRilRequestRaw(byte[], android.os.Message) + */ + public void invokeOemRilRequestRaw(byte data[], Message response); + + +} diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/RawResult.java b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/RawResult.java new file mode 100644 index 00000000..3e2133b9 --- /dev/null +++ b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/RawResult.java @@ -0,0 +1,13 @@ +package com.SecUpwN.AIMSICD.rilexecutor; + +public class RawResult { + public final byte result[]; + public final Throwable exception; + + public RawResult(byte r[], Throwable ex) + { + result = r; + exception = ex; + } + +} diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/SamsungMulticlientRilExecutor.java b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/SamsungMulticlientRilExecutor.java new file mode 100644 index 00000000..d8b7d78f --- /dev/null +++ b/app/src/main/java/com/SecUpwN/AIMSICD/rilexecutor/SamsungMulticlientRilExecutor.java @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2014 Alexey Illarionov + * + * 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.SecUpwN.AIMSICD.rilexecutor; + +import android.annotation.SuppressLint; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.os.Message; +import android.os.Parcel; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.SecUpwN.AIMSICD.BuildConfig; + + +public class SamsungMulticlientRilExecutor implements OemRilExecutor { + + public static final String MULTICLIENT_SOCKET = "Multiclient"; + + public static final int RIL_REQUEST_OEM_RAW = 59; + + public static final int RIL_CLIENT_ERR_SUCCESS = 0; + public static final int RIL_CLIENT_ERR_AGAIN = 1; + public static final int RIL_CLIENT_ERR_INIT = 2; + public static final int RIL_CLIENT_ERR_INVAL = 3; + public static final int RIL_CLIENT_ERR_CONNECT = 4; + public static final int RIL_CLIENT_ERR_IO = 5; + public static final int RIL_CLIENT_ERR_RESPONSE = 6; + public static final int RIL_CLIENT_ERR_UNKNOWN = 7; + + public static final int RESPONSE_SOLICITED = 0; + public static final int RESPONSE_UNSOLICITED = 1; + + private static final boolean DBG = BuildConfig.DEBUG & true; + private static final String TAG = SamsungMulticlientRilExecutor.class.getSimpleName(); + + private volatile LocalSocketThread mThread; + + public SamsungMulticlientRilExecutor() { + } + + @Override + public DetectResult detect() { + String gsmVerRilImpl = ""; + + try { + Class clazz; + clazz = Class.forName("android.os.SystemProperties"); + Method method = clazz.getDeclaredMethod("get", String.class, String.class); + gsmVerRilImpl = (String)method.invoke(null, "gsm.version.ril-impl", ""); + } catch (Exception ignore) { + ignore.printStackTrace(); + } + + if (!gsmVerRilImpl.matches("Samsung\\s+RIL\\(IPC\\).*")) { + return DetectResult.Unavailable("gsm.version.ril-impl = " + gsmVerRilImpl); + } + + LocalSocket s = new LocalSocket(); + try { + s.connect(new LocalSocketAddress(MULTICLIENT_SOCKET)); + } catch (IOException e) { + return DetectResult.Unavailable("Multiclient socket is not available"); + } finally { + try { + s.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return DetectResult.AVAILABLE; + } + + @Override + public synchronized void start() { + if (mThread != null) { + Log.e(TAG, "OEM raw request executor thread is running"); + return; + } + mThread = new LocalSocketThread(MULTICLIENT_SOCKET); + mThread.start(); + } + + @Override + public synchronized void stop() { + if (mThread == null) { + Log.e(TAG, "OEM raw request executor thread is not running"); + return; + } + mThread.cancel(); + mThread = null; + } + + @Override + public synchronized void invokeOemRilRequestRaw(byte[] data, Message response) { + if (mThread == null) { + Log.e(TAG, "OEM raw request executor thread is not running"); + return; + } + try { + mThread.invokeOemRilRequestRaw(data, response); + } catch (IOException ioe) { + Log.e(TAG, "invokeOemRilRequestRaw() error", ioe); + } + } + + public class LocalSocketThread extends Thread { + + private static final int MAX_MESSAGES = 30; + + private final LocalSocketAddress mSocketPath; + private final AtomicBoolean mCancelRequested = new AtomicBoolean(); + + private LocalSocket mSocket; + private volatile InputStream mInputStream; + private volatile OutputStream mOutputStream; + + private final Random mTokenGen = new Random(); + private final Map<Integer, Message> mMessages; + + @SuppressLint("UseSparseArrays") + public LocalSocketThread(String socketPath) { + mSocketPath = new LocalSocketAddress(socketPath); + + mInputStream = null; + mOutputStream = null; + mMessages = new HashMap<Integer, Message>(); + } + + public void cancel() { + if (DBG) Log.v(TAG, "cancel()"); + synchronized (this) { + mCancelRequested.set(true); + disconnect(); + notifyAll(); + } + } + + public synchronized void invokeOemRilRequestRaw(byte[] data, Message response) throws IOException { + int token; + if (mMessages.size() > MAX_MESSAGES) { + Log.e(TAG, "message queue is full"); + return; + } + + if (mOutputStream == null) { + Log.e(TAG, "Local write() error: not connected"); + return; + } + + do { + token = mTokenGen.nextInt(); + } while (mMessages.containsKey(token)); + + byte req[] = marshallRequest(token, data); + + if (DBG) Log.v(TAG, String.format("invokeOemRilRequestRaw() token: 0x%X, header: %s, req: %s ", + token, HexDump.toHexString(getHeader(req)), HexDump.toHexString(req))); + + mOutputStream.write(getHeader(req)); + mOutputStream.write(req); + mMessages.put(token, response); + } + + private byte[] getHeader(byte data[]) { + int len = data.length; + return new byte[]{ + (byte) ((len >> 24) & 0xff), + (byte) ((len >> 16) & 0xff), + (byte) ((len >> 8) & 0xff), + (byte) (len & 0xff) + }; + } + + private byte[] marshallRequest(int token, byte data[]) { + Parcel p = Parcel.obtain(); + p.writeInt(RIL_REQUEST_OEM_RAW); + p.writeInt(token); + p.writeByteArray(data); + byte[] res = p.marshall(); + p.recycle(); + return res; + } + + public synchronized void disconnect() { + if (DBG) + Log.v(TAG, "Local disconnect()"); + + if (mSocket == null) + return; + + try { + mSocket.shutdownInput(); + } catch (IOException e) { + Log.e(TAG, "Local shutdownInput() of mSocket failed", e); + } + + try { + mSocket.shutdownOutput(); + } catch (IOException e) { + Log.e(TAG, "Local shutdownOutput() of mSocket failed", e); + } + + try { + mInputStream.close(); + } catch (IOException e) { + Log.e(TAG, "Local close() of mInputStream failed", e); + } + + try { + mOutputStream.close(); + } catch (IOException e) { + Log.e(TAG, "Local close() of mOutputStream failed", e); + } + + try { + mSocket.close(); + } catch (IOException e) { + Log.e(TAG, "Local close() of mSocket failed", e); + } + + mSocket = null; + mInputStream = null; + mOutputStream = null; + } + + @Override + public void run() { + int rcvd; + int endpos = 0; + final byte buf[] = new byte[4096]; + + Log.i(TAG, "BEGIN LocalSocketThread-Socket"); + setName("MultiClientThread"); + + mSocket = new LocalSocket(); + try { + mSocket.connect(mSocketPath); + mInputStream = mSocket.getInputStream(); + mOutputStream = mSocket.getOutputStream(); + } catch (IOException e) { + Log.e(TAG, "Connect error", e); + return; + } + + while (!mCancelRequested.get()) { + try { + rcvd = mInputStream.read(buf, endpos, buf.length - endpos); + if (rcvd < 0) { + if (DBG) Log.v(TAG, "EOF reached"); + break; + } + endpos += rcvd; + if (endpos < 4) continue; + + int msgLen = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] & 0xff); + if (msgLen + 4 > buf.length) { + Log.e(TAG, "message to big. Length: " + msgLen); + endpos = 0; + continue; + } + if (endpos >= msgLen + 4) { + processRxPacket(buf, 4, msgLen); + int secondPktPos = msgLen + 4; + if (secondPktPos != endpos) { + System.arraycopy(buf, secondPktPos, buf, 0, endpos - secondPktPos); + } + endpos -= msgLen + 4; + } + + if (endpos == buf.length) endpos = 0; + } catch (IOException e) { + disconnect(); + } + } + disconnect(); + } + + private synchronized void processRxPacket(byte data[], int pos, int length) { + int responseType; + Parcel p; + + if (DBG) Log.v(TAG, "received " + length + " bytes: " + HexDump.toHexString(data, pos, length)); + + p = Parcel.obtain(); + try { + p.unmarshall(data, pos, length); + p.setDataPosition(0); + + responseType = p.readInt(); + switch (responseType) { + case RESPONSE_UNSOLICITED: + Log.v(TAG, "Unsolicited response "); + break; + case RESPONSE_SOLICITED: + processSolicited(p); + break; + default: + Log.v(TAG, "Invalid response type: " + responseType); + break; + } + } finally { + p.recycle(); + } + } + + private int processSolicited(Parcel p) { + Integer token = null; + byte responseData[] = null; + Exception errorEx = null; + + try { + token = p.readInt(); + int err = p.readInt(); + + if (DBG) Log.v(TAG, String.format("processSolicited() token: 0x%X err: %d", token , err)); + + if (err != RIL_CLIENT_ERR_SUCCESS) { + throw new RemoteException("remote error " + err); + } + + responseData = p.createByteArray(); + } catch (Exception ex) { + errorEx = ex; + } + + if (token == null) { + Log.e(TAG, "token is null", errorEx); + } else { + synchronized (this) { + Message m = mMessages.remove(token); + if (m != null) { + m.obj = new RawResult(responseData, errorEx); + m.sendToTarget(); + } else { + Log.i(TAG, "Message with token " + token + " not found"); + } + } + } + return RIL_CLIENT_ERR_SUCCESS; + } + } + + public static class RemoteException extends Exception { + + public RemoteException() { + } + + public RemoteException(String detailMessage) { + super(detailMessage); + } + } + +} diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/service/AimsicdService.java b/app/src/main/java/com/SecUpwN/AIMSICD/service/AimsicdService.java index 74ce02b8..20143367 100644 --- a/app/src/main/java/com/SecUpwN/AIMSICD/service/AimsicdService.java +++ b/app/src/main/java/com/SecUpwN/AIMSICD/service/AimsicdService.java @@ -36,6 +36,24 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +/* + * Portions of this software have been copied and modified from + * https://github.com/illarionov/SamsungRilMulticlient + * Copyright (C) 2014 Alexey Illarionov + * + * 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.SecUpwN.AIMSICD.service; import android.app.AlertDialog; @@ -52,7 +70,12 @@ import android.location.LocationListener; import android.location.LocationManager; import android.os.Binder; import android.os.Bundle; +import android.os.ConditionVariable; +import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; import android.support.v4.app.NotificationCompat; import android.telephony.CellLocation; @@ -69,10 +92,20 @@ import com.SecUpwN.AIMSICD.AIMSICD; import com.SecUpwN.AIMSICD.AIMSICDDbAdapter; import com.SecUpwN.AIMSICD.R; import com.SecUpwN.AIMSICD.Helpers; - +import com.SecUpwN.AIMSICD.OemCommands; +import com.SecUpwN.AIMSICD.rilexecutor.DetectResult; +import com.SecUpwN.AIMSICD.rilexecutor.OemRilExecutor; +import com.SecUpwN.AIMSICD.rilexecutor.RawResult; +import com.SecUpwN.AIMSICD.rilexecutor.SamsungMulticlientRilExecutor; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Timer; public class AimsicdService extends Service implements OnSharedPreferenceChangeListener { @@ -104,6 +137,7 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL private int mCellID = -1; private int mSID = -1; private int mTimingAdvance = -1; + private int mNeighbouringCellSize = -1; private double mLongitude = 0.0; private double mLatitude = 0.0; private String mNetType = ""; @@ -125,18 +159,43 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL private String mSimSubs = ""; private String mDataActivityType = ""; private String mDataActivityTypeShort = ""; - private Map<Integer,Integer> mNeighborMapUMTS = new HashMap<Integer,Integer>(); - private Map<String,Integer> mNeighborMapGSM = new HashMap<String,Integer>(); + private final Map<Integer,Integer> mNeighborMapUMTS = new HashMap<Integer,Integer>(); + private final Map<String,Integer> mNeighborMapGSM = new HashMap<String,Integer>(); /* * Tracking and Alert Declarations */ private boolean mRoaming; - public boolean TrackingCell; - public boolean TrackingLocation; - public boolean TrackingFemtocell; + private boolean TrackingCell; + private boolean TrackingLocation; + private boolean TrackingFemtocell; private boolean mFemtoDetected; + /* + * Samsung MultiRil Implementation + */ + private static final int ID_REQUEST_START_SERVICE_MODE_COMMAND = 1; + private static final int ID_REQUEST_FINISH_SERVICE_MODE_COMMAND = 2; + private static final int ID_REQUEST_PRESS_A_KEY = 3; + private static final int ID_REQUEST_REFRESH = 4; + + private static final int ID_RESPONSE = 101; + private static final int ID_RESPONSE_FINISH_SERVICE_MODE_COMMAND = 102; + private static final int ID_RESPONSE_PRESS_A_KEY = 103; + + private static final int REQUEST_TIMEOUT = 10000; // ms + private static final int REQUEST_VERSION_TIMEOUT = 300; // ms + private final ConditionVariable mRequestCondvar = new ConditionVariable(); + private final Object mLastResponseLock = new Object(); + + private volatile List<String> mLastResponse; + + private DetectResult mRilExecutorDetectResult; + private OemRilExecutor mRequestExecutor; + + private HandlerThread mHandlerThread; + private Handler mHandler; + @Override public IBinder onBind(Intent intent) { setNotification(); @@ -162,6 +221,22 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL refreshDeviceInfo(); setNotification(); + //Sumsung MultiRil Initialization + mHandlerThread = new HandlerThread("ServiceModeSeqHandler"); + mHandlerThread.start(); + + Looper l = mHandlerThread.getLooper(); + mHandler = new Handler(l, new MyHandler()); + + mRequestExecutor = new SamsungMulticlientRilExecutor(); + mRilExecutorDetectResult = mRequestExecutor.detect(); + if (!mRilExecutorDetectResult.available) { + Log.e(TAG, "Samsung multiclient ril not available: " + mRilExecutorDetectResult.error); + mRequestExecutor = null; + } else { + mRequestExecutor.start(); + } + Log.i(TAG, "Service launched successfully"); } @@ -177,9 +252,168 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL prefs.unregisterOnSharedPreferenceChangeListener(this); cancelNotification(); dbHelper.close(); + + //Samsung MultiRil Cleanup + if (mRequestExecutor != null) { + mRequestExecutor.stop(); + mRequestExecutor = null; + } + mHandler = null; + mHandlerThread.quit(); + mHandlerThread = null; Log.i(TAG, "Service destroyed"); } + public DetectResult getRilExecutorStatus() { + return mRilExecutorDetectResult; + } + + public List<String> getCipheringInfo() { + return executeServiceModeCommand( + OemCommands.OEM_SM_TYPE_TEST_MANUAL, + OemCommands.OEM_SM_TYPE_SUB_CIPHERING_PROTECTION_ENTER, + null + ); + } + + public List<String> getNeighbours() { + KeyStep getNeighboursKeySeq[] = new KeyStep[]{ + new KeyStep('\0', false), + new KeyStep('1', false), // [1] DEBUG SCREEN + new KeyStep('4', true), // [4] NEIGHBOUR CELL + }; + + return executeServiceModeCommand( + OemCommands.OEM_SM_TYPE_TEST_MANUAL, + OemCommands.OEM_SM_TYPE_SUB_ENTER, + Arrays.asList(getNeighboursKeySeq) + ); + + } + + private List<String> executeServiceModeCommand(int type, int subtype, + java.util.Collection<KeyStep> keySeqence) { + return executeServiceModeCommand(type, subtype, keySeqence, REQUEST_TIMEOUT); + } + + private synchronized List<String> executeServiceModeCommand(int type, int subtype, + java.util.Collection<KeyStep> keySeqence, int timeout) { + if (mRequestExecutor == null) return Collections.emptyList(); + + mRequestCondvar.close(); + mHandler.obtainMessage(ID_REQUEST_START_SERVICE_MODE_COMMAND, + type, + subtype, + keySeqence).sendToTarget(); + if (!mRequestCondvar.block(timeout)) { + Log.e(TAG, "request timeout"); + return Collections.emptyList(); + } else { + synchronized (mLastResponseLock) { + return mLastResponse; + } + } + } + + private static class KeyStep { + public final char keychar; + public boolean captureResponse; + + public KeyStep(char keychar, boolean captureResponse) { + this.keychar = keychar; + this.captureResponse = captureResponse; + } + + public static KeyStep KEY_START_SERVICE_MODE = new KeyStep('\0', true); + } + + private class MyHandler implements Handler.Callback { + + private int mCurrentType; + private int mCurrentSubtype; + + private Queue<KeyStep> mKeySequence; + + @Override + public boolean handleMessage(Message msg) { + byte[] requestData; + Message responseMsg; + KeyStep lastKeyStep; + + switch (msg.what) { + case ID_REQUEST_START_SERVICE_MODE_COMMAND: + mCurrentType = msg.arg1; + mCurrentSubtype = msg.arg2; + mKeySequence = new ArrayDeque<KeyStep>(3); + if (msg.obj != null) { + mKeySequence.addAll((java.util.Collection<KeyStep>) msg.obj); + } else { + mKeySequence.add(KeyStep.KEY_START_SERVICE_MODE); + } + synchronized (mLastResponseLock) { + mLastResponse = new ArrayList<>(); + } + requestData = OemCommands.getEnterServiceModeData( + mCurrentType, mCurrentSubtype, OemCommands.OEM_SM_ACTION); + responseMsg = mHandler.obtainMessage(ID_RESPONSE); + mRequestExecutor.invokeOemRilRequestRaw(requestData, responseMsg); + break; + case ID_REQUEST_FINISH_SERVICE_MODE_COMMAND: + requestData = OemCommands.getEndServiceModeData(mCurrentType); + responseMsg = mHandler.obtainMessage(ID_RESPONSE_FINISH_SERVICE_MODE_COMMAND); + mRequestExecutor.invokeOemRilRequestRaw(requestData, responseMsg); + break; + case ID_REQUEST_PRESS_A_KEY: + requestData = OemCommands.getPressKeyData(msg.arg1, OemCommands.OEM_SM_ACTION); + responseMsg = mHandler.obtainMessage(ID_RESPONSE_PRESS_A_KEY); + mRequestExecutor.invokeOemRilRequestRaw(requestData, responseMsg); + break; + case ID_REQUEST_REFRESH: + requestData = OemCommands.getPressKeyData('\0', OemCommands.OEM_SM_QUERY); + responseMsg = mHandler.obtainMessage(ID_RESPONSE); + mRequestExecutor.invokeOemRilRequestRaw(requestData, responseMsg); + break; + case ID_RESPONSE: + lastKeyStep = mKeySequence.poll(); + try { + RawResult result = (RawResult) msg.obj; + if (result == null) { + Log.e(TAG, "result is null"); + break; + } + if (result.exception != null) { + Log.e(TAG, "", result.exception); + break; + } + if (result.result == null) { + Log.v(TAG, "No need to refresh."); + break; + } + if (lastKeyStep.captureResponse) { + synchronized (mLastResponseLock) { + mLastResponse.addAll(Helpers.unpackListOfStrings(result.result)); + } + } + } finally { + if (mKeySequence.isEmpty()) { + mHandler.obtainMessage(ID_REQUEST_FINISH_SERVICE_MODE_COMMAND).sendToTarget(); + } else { + mHandler.obtainMessage(ID_REQUEST_PRESS_A_KEY, mKeySequence.element().keychar, 0).sendToTarget(); + } + } + break; + case ID_RESPONSE_PRESS_A_KEY: + mHandler.sendMessageDelayed(mHandler.obtainMessage(ID_REQUEST_REFRESH), 10); + break; + case ID_RESPONSE_FINISH_SERVICE_MODE_COMMAND: + mRequestCondvar.open(); + break; + + } + return true; + } + } + /** * Process User Preferences */ @@ -301,6 +535,34 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL return new double[] {mLatitude, mLongitude}; } + /** + * Tracking Cell Information + * + * @return boolean indicating Cell Information Tracking State + */ + public boolean isTrackingCell() { + return TrackingCell; + } + + /** + * Tracking Location Information + * + * @return boolean indicating Location Tracking State + */ + public boolean isTrackingLocation() { + return TrackingLocation; + } + + /** + * Tracking Femotcell Connections + * + * @return boolean indicating Femtocell Connection Tracking State + */ + public boolean isTrackingFemtocell() { + return TrackingFemtocell; + } + + /** * LTE Timing Advance * @@ -356,9 +618,10 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL public String getSimCountry(boolean force) { if (mSimCountry.isEmpty() || force) { try { - mSimCountry = tm.getSimCountryIso(); + mSimCountry = (tm.getSimCountryIso() != null) ? tm.getSimCountryIso() : ""; } catch (Exception e) { //SIM methods can cause Exceptions on some devices + Log.e(TAG, "getSimCountry " + e); } } @@ -373,9 +636,10 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL public String getSimOperator(boolean force) { if (mSimOperator.isEmpty() || force) { try { - mSimOperator = tm.getSimOperator(); + mSimOperator = (tm.getSimOperator() != null) ? tm.getSimOperator() : ""; } catch (Exception e) { //SIM methods can cause Exceptions on some devices + Log.e(TAG, "getSimOperator " + e); } } @@ -390,7 +654,7 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL public String getSimOperatorName(boolean force) { if (mSimOperatorName.isEmpty() || force) { try { - mSimOperatorName = tm.getSimOperatorName(); + mSimOperatorName = (tm.getSimOperatorName() != null) ? tm.getSimOperatorName() : ""; }catch (Exception e) { //SIM methods can cause Exceptions on some devices } @@ -407,9 +671,10 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL public String getSimSubs(boolean force) { if (mSimSubs.isEmpty() || force) { try { - mSimSubs = tm.getSubscriberId(); + mSimSubs = (tm.getSubscriberId() != null) ? tm.getSubscriberId() : ""; } catch (Exception e) { //Some devices don't like this method + Log.e(TAG, "getSimSubs " + e); } } @@ -425,9 +690,10 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL public String getSimSerial(boolean force) { if (mSimSerial.isEmpty() || force) { try { - mSimSerial = tm.getSimSerialNumber(); + mSimSerial = (tm.getSimSerialNumber() != null) ? tm.getSimSerialNumber() : ""; } catch (Exception e) { //SIM methods can cause Exceptions on some devices + Log.e(TAG, "getSimSerial " + e); } } @@ -497,18 +763,20 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL public String getPhoneNumber(boolean force) { if (mPhoneNum.isEmpty() || force) { try { - mPhoneNum = tm.getLine1Number(); - } catch (NullPointerException npe) { + mPhoneNum = (tm.getLine1Number() != null) ? tm.getLine1Number() : ""; + } catch (Exception e) { //Sim does not hold line number + Log.e(TAG, "getPhoneNumber (1) " + e); } } //Check if Phone Number successfully retrieved and if not try subscriber if (mPhoneNum.isEmpty()) try { - mPhoneNum = tm.getSubscriberId(); - } catch (NullPointerException npe) { + mPhoneNum = (tm.getSubscriberId() != null) ? tm.getSubscriberId() : ""; + } catch (Exception e) { //Seems some devices don't like this on either + Log.e(TAG, "getPhoneNumber (2) " + e); } @@ -813,6 +1081,60 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL } } + /** + * Updates Neighbouring Cell details for either GSM or UMTS networks + * + */ + public void updateNeighbouringCells() { + //Update Neighbouring Cell Map + for (String key: mNeighborMapGSM.keySet()) + mNeighborMapGSM.put(key,-113); + for (int key: mNeighborMapUMTS.keySet()) + mNeighborMapUMTS.put(key,-115); + + List<NeighboringCellInfo> neighboringCellInfo; + neighboringCellInfo = tm.getNeighboringCellInfo(); + mNeighbouringCellSize = neighboringCellInfo.size(); + for (NeighboringCellInfo i : neighboringCellInfo) { + int networktype = i.getNetworkType(); + if ((networktype == TelephonyManager.NETWORK_TYPE_UMTS) || + (networktype == TelephonyManager.NETWORK_TYPE_HSDPA) || + (networktype == TelephonyManager.NETWORK_TYPE_HSUPA) || + (networktype == TelephonyManager.NETWORK_TYPE_HSPA)) + mNeighborMapUMTS.put(i.getPsc(), i.getRssi()-115); + else + mNeighborMapGSM.put(i.getLac()+"-"+i.getCid(), (-113+2*(i.getRssi()))); + } + } + + /** + * Neighbouring GSM Cell Map + * + * @return Map of GSM Neighbouring Cell Information + */ + public Map getGSMNeighbouringCells() { + return mNeighborMapGSM; + } + + /** + * Neighbouring UMTS Cell Map + * + * @return Map of UMTS Neighbouring Cell Information + */ + public Map getUMTSNeighbouringCells() { + return mNeighborMapUMTS; + } + + /** + * Neighbouring Cell Size + * + * @return Integer of Neighbouring Cell Size + */ + public int getNeighbouringCellSize() { + return mNeighbouringCellSize; + } + + /** * Cell Information Tracking and database logging * @@ -866,9 +1188,25 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL mSimOperator = getSimOperator(true); mSimOperatorName = getNetworkName(true); mSID = getSID(); + + //Update location through CDMA if not tracking through GPS + if (!TrackingLocation) { + int Long = cdmaCellLocation.getBaseStationLongitude(); + int Lat = cdmaCellLocation.getBaseStationLatitude(); + + if (!(Double.isNaN(Long) || Long < -2592000 || Long > 2592000)) { + mLongitude = ((double) Long) / (3600 * 4); + } + + if (!(Double.isNaN(Lat) || Lat < -2592000 || Lat > 2592000)) { + mLatitude = ((double) Lat) / (3600 * 4); + } + } } } + updateNeighbouringCells(); + if (TrackingCell) { dbHelper.open(); mDbResult = dbHelper.insertCell(mLacID, mCellID, @@ -899,18 +1237,7 @@ public class AimsicdService extends Service implements OnSharedPreferenceChangeL for (int key: mNeighborMapUMTS.keySet()) mNeighborMapUMTS.put(key,-115); - List<NeighboringCellInfo> neighboringCellInfo; - neighboringCellInfo = tm.getNeighboringCellInfo(); - for (NeighboringCellInfo i : neighboringCellInfo) { - int networktype = i.getNetworkType(); - if ((networktype == TelephonyManager.NETWORK_TYPE_UMTS) || - (networktype == TelephonyManager.NETWORK_TYPE_HSDPA) || - (networktype == TelephonyManager.NETWORK_TYPE_HSUPA) || - (networktype == TelephonyManager.NETWORK_TYPE_HSPA)) - mNeighborMapUMTS.put(i.getPsc(), i.getRssi()-115); - else - mNeighborMapGSM.put(i.getLac()+"-"+i.getCid(), (-113+2*(i.getRssi()))); - } + updateNeighbouringCells(); if (TrackingCell) { dbHelper.open(); diff --git a/app/src/main/res/layout/cell_fragment.xml b/app/src/main/res/layout/cell_fragment.xml new file mode 100644 index 00000000..b288f833 --- /dev/null +++ b/app/src/main/res/layout/cell_fragment.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <Button + android:id="@+id/button_refresh" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/button_refresh" android:layout_below="@+id/progress"/> + + <TableLayout + android:id="@+id/cellView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:stretchColumns="1" android:layout_below="@+id/button_refresh"> + <TableRow + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:gravity="center_horizontal" + android:background="@color/black"> + <TextView + android:layout_width="match_parent" android:layout_height="wrap_content" + android:textSize="18sp" android:text="@string/neighbouring_cells_title" + android:layout_span="2" + android:background="@color/black" + android:textColor="@color/green_text" + android:padding="4dip"/> + </TableRow> + <TableRow + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:background="@color/black"> + + <TextView + android:id="@+id/neighbouring_number_label" + android:text="@string/neighbouring_cells_num_title" + android:background="@color/black" + android:textColor="@color/white" + android:padding="2dip" android:gravity="right" + android:layout_gravity="right" + android:layout_width="fill_parent"/> + + <TextView + android:id="@+id/neighbouring_number" android:text="@string/empty" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="2dip" android:gravity="left"/> + </TableRow> + <TableRow + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:gravity="left" + android:background="@color/black"> + + <TextView + android:id="@+id/neighbouring_cells" android:text="@string/empty" + android:layout_weight="1" android:background="@color/black" + android:textColor="@color/white" + android:padding="2dip" + android:layout_gravity="left" + android:layout_width="fill_parent" + android:layout_span="2"/> + </TableRow> + <TableRow + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:gravity="center_horizontal" + android:background="@color/black"> + <TextView + android:id="@+id/ciphering_indicator_title" + android:layout_width="match_parent" android:layout_height="wrap_content" + android:textSize="18sp" android:text="@string/ciphering_indicator_title" + android:layout_span="2" + android:background="@color/black" + android:textColor="@color/green_text" + android:padding="4dip" android:visibility="gone"/> + </TableRow> + <TableRow + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:gravity="left" + android:background="@color/black"> + + <TextView + android:id="@+id/ciphering_indicator" android:text="@string/empty" + android:layout_weight="1" android:background="@color/black" + android:textColor="@color/white" + android:padding="2dip" + android:layout_gravity="left" + android:layout_width="fill_parent" + android:layout_span="2" android:visibility="gone"/> + </TableRow> + </TableLayout> +</RelativeLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/db_view.xml b/app/src/main/res/layout/db_view.xml index 82cb9634..7088488d 100644 --- a/app/src/main/res/layout/db_view.xml +++ b/app/src/main/res/layout/db_view.xml @@ -1,27 +1,26 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" android:id="@+id/linearLayout"> + android:orientation="horizontal" + android:id="@+id/linearLayout"> <Spinner android:layout_width="269dp" android:layout_height="wrap_content" android:id="@+id/table_spinner" android:entries="@array/table_names" - android:prompt="@string/table_names_prompt" - tools:context="com.SecUpwN.AIMSICD.DbViewerFragment"/> + android:prompt="@string/table_names_prompt"/> <Button android:id="@+id/load_table_data" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:text="@string/load_table_data" - tools:context="com.SecUpwN.AIMSICD.DbViewerFragment"/> + android:text="@string/load_table_data"/> </LinearLayout> <ListView @@ -33,6 +32,7 @@ android:listSelector="@android:color/transparent" android:cacheColorHint="@android:color/transparent" android:divider="@null" - android:dividerHeight="10dp" android:layout_below="@+id/linearLayout"/> + android:dividerHeight="10dp" + android:layout_below="@+id/linearLayout"/> </RelativeLayout> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b8cf3331..d970f45b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -66,6 +66,10 @@ <string name="device_info_title">Device Information</string> <string name="sim_info_title">SIM Information</string> <string name="network_info">Network Information</string> + <string name="cell_info_title">Cell Information</string> + <string name="neighbouring_cells_title">Neighbouring Cells</string> + <string name="ciphering_indicator_title">Ciphering Indicator</string> + <string name="neighbouring_cells_num_title">Total:</string> <string name="sim_country">Country:</string> <string name="sim_operator_id">Operator ID:</string> <string name="sim_operator_name">Operator Name:</string> @@ -84,6 +88,7 @@ <string name="data_status">Data Status:</string> <string name="network_roaming">Roaming:</string> <string name="network_lte_timing_advance">Timing Advance:</string> + <string name="button_refresh">Refresh</string> <!-- CDMA LAYOUT --> <string name="network_netid">Network ID:</string> @@ -101,9 +106,9 @@ <string name="pref_autostart_title">Auto Start</string> <string name="pref_autostart_key">pref_autostart</string> <string name="pref_autostart_summ">Automatically start service on boot</string> - <string name="pref_killservice_title">Disable Persistent Service</string> + <string name="pref_killservice_title">Persistent Service</string> <string name="pref_killservice_key">pref_kill_service</string> - <string name="pref_killservice_summ">Disable the Persistent Service when exiting the application</string> + <string name="pref_killservice_summ">Service will continue to run after exiting the application</string> <string name="pref_protection_title">Protection Settings</string> <string name="pref_enable_cell_key">pref_enable_cell</string> <string name="pref_enable_cell_title">Cell Tracking</string> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d1077d37..cd5c83d0 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -14,7 +14,7 @@ android:key="@string/pref_killservice_key" android:title="@string/pref_killservice_title" android:summary="@string/pref_killservice_summ" - android:defaultValue="false" /> + android:defaultValue="true" /> <CheckBoxPreference android:key="@string/pref_enable_cell_key" android:title="@string/pref_enable_cell_title" -- GitLab From a75da89516ffbbf5439a516287c0744d837a1203 Mon Sep 17 00:00:00 2001 From: xLaMbChOpSx <ryanskungfu@me.com> Date: Tue, 6 May 2014 23:29:23 +1000 Subject: [PATCH 2/2] Initial Commit for About Dialog Still needs to be finished but the About dialog has been started, I wanted to push the changes for comment and will complete all functions including the hyperlink functionality and remaining details if this form is approved. --- .../java/com/SecUpwN/AIMSICD/AIMSICD.java | 2 + .../com/SecUpwN/AIMSICD/AboutFragment.java | 43 +++++++++ app/src/main/res/layout/about_fragment.xml | 95 +++++++++++++++++++ app/src/main/res/values/strings.xml | 17 ++++ 4 files changed, 157 insertions(+) create mode 100644 app/src/main/java/com/SecUpwN/AIMSICD/AboutFragment.java create mode 100644 app/src/main/res/layout/about_fragment.xml diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/AIMSICD.java b/app/src/main/java/com/SecUpwN/AIMSICD/AIMSICD.java index 6e2078a2..0faf997a 100644 --- a/app/src/main/java/com/SecUpwN/AIMSICD/AIMSICD.java +++ b/app/src/main/java/com/SecUpwN/AIMSICD/AIMSICD.java @@ -518,6 +518,8 @@ public class AIMSICD extends FragmentActivity { titles.add(getString(R.string.db_viewer)); fragments.add(new CellInfoFragment(mContext)); titles.add(getString(R.string.cell_info_title)); + fragments.add(new AboutFragment(mContext)); + titles.add(getString(R.string.about_aimsicd)); } @Override public Fragment getItem(int position) { diff --git a/app/src/main/java/com/SecUpwN/AIMSICD/AboutFragment.java b/app/src/main/java/com/SecUpwN/AIMSICD/AboutFragment.java new file mode 100644 index 00000000..5c7d6341 --- /dev/null +++ b/app/src/main/java/com/SecUpwN/AIMSICD/AboutFragment.java @@ -0,0 +1,43 @@ +package com.SecUpwN.AIMSICD; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class AboutFragment extends Fragment { + + private Context mContext; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.about_fragment, container, false); + String version; + + PackageManager manager = mContext.getPackageManager(); + try { + PackageInfo info = manager.getPackageInfo(mContext.getPackageName(), 0); + version = info.versionName; + } catch (PackageManager.NameNotFoundException nnfe) { + //Woops something went wrong?? + version = ""; + } + + TextView versionNumber = (TextView) v.findViewById(R.id.aimsicd_version); + versionNumber.setText(version); + + return v; + } + + public AboutFragment (Context context) { + mContext = context; + } + + +} diff --git a/app/src/main/res/layout/about_fragment.xml b/app/src/main/res/layout/about_fragment.xml new file mode 100644 index 00000000..57c4cb6e --- /dev/null +++ b/app/src/main/res/layout/about_fragment.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:layout_width="match_parent" android:layout_height="wrap_content" + android:textSize="18sp" android:text="@string/about_aimsicd" + android:background="@color/black" + android:textColor="@color/green_text" + android:padding="4dip" android:id="@+id/textView"/> + + <TextView + android:id="@+id/aimsicd_about_info" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/aimsicd_about_info" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="8dip" android:layout_below="@+id/textView"/> + <TextView + android:id="@+id/aimsicd_version" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/empty" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="8dip" android:layout_below="@+id/textView" + android:layout_toRightOf="@+id/aimsicd_about_info"/> + <TextView + android:id="@+id/aimsicd_about_info2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/aimsicd_about_info2" + android:textStyle="italic" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="8dip" android:layout_below="@+id/aimsicd_version"/> + <TextView + android:id="@+id/aimsicd_poc" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/poc_info" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="8dip" android:layout_below="@+id/aimsicd_about_info2"/> + <TextView + android:id="@+id/aimsicd_poc_link" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/poc_link" + android:autoLink="web" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="8dip" android:layout_below="@+id/aimsicd_about_info2" + android:layout_toRightOf="@+id/aimsicd_poc"/> + <TextView + android:id="@+id/aimsicd_disclaimer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/disclaimer_info" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="8dip" android:layout_below="@+id/aimsicd_poc"/> + <TextView + android:id="@+id/aimsicd_disclaimer_link" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/disclaimer_link" + android:autoLink="web" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="8dip" android:layout_below="@+id/aimsicd_poc" + android:layout_toRightOf="@+id/aimsicd_disclaimer"/> + <TextView + android:id="@+id/aimsicd_contribute" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/github_info" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="8dip" android:layout_below="@+id/aimsicd_disclaimer"/> + <TextView + android:id="@+id/aimsicd_contribute_link" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/aimsicd_github_link" + android:autoLink="web" + android:background="@color/black" + android:textColor="@color/medium_blue" + android:padding="8dip" android:layout_below="@+id/aimsicd_disclaimer" + android:layout_toRightOf="@+id/aimsicd_contribute"/> + +</RelativeLayout> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d970f45b..6998e111 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -90,6 +90,23 @@ <string name="network_lte_timing_advance">Timing Advance:</string> <string name="button_refresh">Refresh</string> + <!-- ABOUT LAYOUT --> + <string name="about_aimsicd">About AIMSICD</string> + <string name="aimsicd_about_info">Android IMSI-Catcher Detector - </string> + <string name="aimsicd_about_info2">Detect and avoid IMSI-Catcher attacks!</string> + <string name="poc_info">This App is a</string> + <string name="poc_link"><a href="https://en.wikipedia.org/wiki/Proof_of_concept">PoC</a></string> + <string name="disclaimer_info">You MUST accept our</string> + <string name="disclaimer_link"><a href="https://github.com/SecUpwN/Android-IMSI-Catcher-Detector/blob/master/DISCLAIMER">Disclaimer</a></string> + <string name="github_info">Contribute to the AIMSICD-Project on</string> + <string name="aimsicd_github_link"><a href="https://github.com/SecUpwN/Android-IMSI-Catcher-Detector">Github</a></string> + <string name="aimsicd_xda_link"><a href="http://forum.xda-developers.com/showthread.php?t=1422969">Official XDA Development Thread</a></string> + <string name="aimsicd_release_link"><a href="https://github.com/SecUpwN/Android-IMSI-Catcher-Detector/releases">WIP-Release of AIMSICD</a></string> + <string name="aimsicd_changelog_link"><a href="https://github.com/SecUpwN/Android-IMSI-Catcher-Detector/blob/master/CHANGELOG.md">Changelog</a></string> + <string name="aimsicd_license_link"><a href="https://github.com/SecUpwN/Android-IMSI-Catcher-Detector/blob/master/LICENSE">GPL v3+</a></string> + <string name="aimsicd_credits_link"><a href="https://github.com/SecUpwN/Android-IMSI-Catcher-Detector/blob/master/CREDITS">CREDITS</a></string> + + <!-- CDMA LAYOUT --> <string name="network_netid">Network ID:</string> <string name="network_sysid">System ID:</string> -- GitLab