-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,496 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
app/src/main/java/cc/ioctl/nfcdevicehost/activity/JumpEntryActivity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package cc.ioctl.nfcdevicehost.activity; | ||
|
||
import android.content.ComponentName; | ||
import android.content.Intent; | ||
import android.os.Bundle; | ||
|
||
import androidx.annotation.Nullable; | ||
|
||
/** | ||
* External file share/browser springboard entry point. | ||
* This activity is short-lived and only used to launch a target activity. | ||
* This is a transient Activity, and will be finished immediately after launching the target activity. | ||
*/ | ||
public class JumpEntryActivity extends BaseActivity { | ||
|
||
@Override | ||
protected boolean doOnCreate(@Nullable Bundle savedInstanceState) { | ||
Intent intent = getIntent(); | ||
String action = intent.getAction(); | ||
if (Intent.ACTION_VIEW.equals(action)) { | ||
Intent targetIntent = new Intent(intent); | ||
targetIntent.setComponent(new ComponentName(this, SidebandHostActivity.class)); | ||
startActivity(targetIntent); | ||
} | ||
finish(); | ||
return false; | ||
} | ||
|
||
@Override | ||
protected boolean shouldRetainActivitySavedInstanceState() { | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
app/src/main/java/cc/ioctl/nfcdevicehost/activity/SidebandHostActivity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package cc.ioctl.nfcdevicehost.activity; | ||
|
||
import android.content.Intent; | ||
import android.net.Uri; | ||
import android.os.Bundle; | ||
import android.view.Menu; | ||
import android.view.MenuItem; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
import androidx.appcompat.app.ActionBar; | ||
|
||
import cc.ioctl.nfcdevicehost.R; | ||
import cc.ioctl.nfcdevicehost.activity.ui.dump.HalDumpFileViewFragment; | ||
|
||
/** | ||
* Fragment host activity for the secondary fragments, eg. saved dump viewer. | ||
*/ | ||
public class SidebandHostActivity extends BaseActivity { | ||
|
||
@Override | ||
protected boolean doOnCreate(@Nullable Bundle savedInstanceState) { | ||
super.doOnCreate(savedInstanceState); | ||
setContentView(R.layout.activity_sideband_host); | ||
if (savedInstanceState == null) { | ||
Intent intent = getIntent(); | ||
String action = intent.getAction(); | ||
if (Intent.ACTION_VIEW.equals(action)) { | ||
Uri uri = intent.getData(); | ||
Bundle args = new Bundle(); | ||
args.putString(HalDumpFileViewFragment.EXTRA_CONTENT, uri.toString()); | ||
getSupportFragmentManager() | ||
.beginTransaction() | ||
.replace(R.id.sideband_host_fragment_container, HalDumpFileViewFragment.class, args) | ||
.commit(); | ||
} | ||
} | ||
ActionBar actionBar = getSupportActionBar(); | ||
if (actionBar != null) { | ||
actionBar.setDisplayHomeAsUpEnabled(true); | ||
} | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean onCreateOptionsMenu(Menu menu) { | ||
super.onCreateOptionsMenu(menu); | ||
getMenuInflater().inflate(R.menu.menu_activity_main_ui_common, menu); | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean onOptionsItemSelected(@NonNull MenuItem item) { | ||
if (item.getItemId() == R.id.action_exit) { | ||
finish(); | ||
return true; | ||
} | ||
return super.onOptionsItemSelected(item); | ||
} | ||
} |
120 changes: 120 additions & 0 deletions
120
app/src/main/java/cc/ioctl/nfcdevicehost/activity/ui/dump/BaseHalDumpFragment.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package cc.ioctl.nfcdevicehost.activity.ui.dump; | ||
|
||
import android.view.LayoutInflater; | ||
import android.view.ViewGroup; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.fragment.app.Fragment; | ||
import androidx.recyclerview.widget.RecyclerView; | ||
|
||
import java.util.Date; | ||
import java.util.Locale; | ||
|
||
import cc.ioctl.nfcdevicehost.decoder.NciPacketDecoder; | ||
import cc.ioctl.nfcdevicehost.decoder.NxpHalV2EventTranslator; | ||
import cc.ioctl.nfcdevicehost.util.ByteUtils; | ||
|
||
public abstract class BaseHalDumpFragment extends Fragment { | ||
|
||
public static class NciDumpViewHolder extends RecyclerView.ViewHolder { | ||
public enum ViewType { | ||
TRANSACTION, | ||
IOCTL | ||
} | ||
|
||
public ViewType type; | ||
public cc.ioctl.nfcdevicehost.databinding.ItemMainNciDumpBinding binding; | ||
|
||
public NciDumpViewHolder(cc.ioctl.nfcdevicehost.databinding.ItemMainNciDumpBinding binding, ViewType type) { | ||
super(binding.getRoot()); | ||
this.type = type; | ||
this.binding = binding; | ||
} | ||
} | ||
|
||
protected void updateListViewItem(@NonNull NciDumpViewHolder holder, | ||
@NonNull NxpHalV2EventTranslator.TransactionEvent event) { | ||
long timestamp = event.timestamp; | ||
String seqTime; | ||
Date now = new Date(); | ||
Date seqTimeDate = new Date(timestamp); | ||
if (now.getYear() == seqTimeDate.getYear() && now.getMonth() == seqTimeDate.getMonth() | ||
&& now.getDate() == seqTimeDate.getDate()) { | ||
// the same day, HH:mm:ss.SSS | ||
seqTime = String.format(Locale.ROOT, "#%d ", event.sequence) | ||
+ String.format(Locale.ROOT, "%1$tH:%1$tM:%1$tS.%1$tL", event.timestamp); | ||
} else { | ||
// not the same day, yyyy-MM-dd HH:mm:ss.SSS | ||
seqTime = String.format(Locale.ROOT, "#%d ", event.sequence) | ||
+ String.format(Locale.ROOT, "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL", event.timestamp); | ||
} | ||
String typeText = "<INVALID>"; | ||
StringBuilder dataText = new StringBuilder(); | ||
if (event instanceof NxpHalV2EventTranslator.IoctlTransactionEvent) { | ||
typeText = "IOCTL"; | ||
NxpHalV2EventTranslator.IoctlTransactionEvent ev = (NxpHalV2EventTranslator.IoctlTransactionEvent) event; | ||
dataText.append("request: 0x").append(Integer.toHexString(ev.request)); | ||
dataText.append(" arg: 0x").append(Long.toHexString(ev.arg)); | ||
} else if (event instanceof NxpHalV2EventTranslator.RawTransactionEvent) { | ||
typeText = "???"; | ||
NxpHalV2EventTranslator.RawTransactionEvent ev = (NxpHalV2EventTranslator.RawTransactionEvent) event; | ||
dataText.append(ev.direction); | ||
dataText.append("\n"); | ||
dataText.append(ByteUtils.bytesToHexString(ev.data)); | ||
} else if (event instanceof NxpHalV2EventTranslator.NciTransactionEvent) { | ||
NxpHalV2EventTranslator.NciTransactionEvent ev = (NxpHalV2EventTranslator.NciTransactionEvent) event; | ||
switch (ev.type) { | ||
case NCI_DATA: { | ||
NciPacketDecoder.NciDataPacket pk = (NciPacketDecoder.NciDataPacket) ev.packet; | ||
typeText = "DAT"; | ||
dataText.append("conn: ").append(pk.connId).append(" ").append("credits: ").append(pk.credits); | ||
dataText.append("\npayload(").append(pk.data.length).append("): \n"); | ||
dataText.append(ByteUtils.bytesToHexString(pk.data)); | ||
break; | ||
} | ||
case NCI_NTF: | ||
case NCI_RSP: | ||
case NCI_CMD: { | ||
NciPacketDecoder.NciControlPacket pk = (NciPacketDecoder.NciControlPacket) ev.packet; | ||
typeText = (pk.type == NciPacketDecoder.Type.NCI_CMD) ? "CMD" | ||
: ((pk.type == NciPacketDecoder.Type.NCI_NTF) ? "NTF" : "RSP"); | ||
int msgType = pk.type.getInt(); | ||
String name = NciPacketDecoder.getNciOperationName(msgType, pk.groupId, pk.opcodeId); | ||
if (name == null) { | ||
name = "Unknown"; | ||
if (NciPacketDecoder.isNciOperationProprietary(msgType, pk.groupId, pk.opcodeId)) { | ||
name += " Proprietary"; | ||
} | ||
name += " " + String.format(Locale.ROOT, "GID:0x%02X", pk.groupId) | ||
+ " " + String.format(Locale.ROOT, "OID:0x%02X", pk.opcodeId); | ||
} else { | ||
name += " (" + String.format(Locale.ROOT, "0x%02X", pk.groupId) | ||
+ "/" + String.format(Locale.ROOT, "0x%02X", pk.opcodeId) + ")"; | ||
} | ||
dataText.append(name); | ||
dataText.append("\npayload(").append(pk.data.length).append(")\n"); | ||
dataText.append(ByteUtils.bytesToHexString(pk.data)); | ||
break; | ||
} | ||
default: { | ||
typeText = "<UNKNOWN>"; | ||
dataText.append(ev.packet.toString()); | ||
} | ||
} | ||
} | ||
holder.binding.textViewItemNciDumpTime.setText(seqTime); | ||
holder.binding.textViewItemNciDumpType.setText(typeText); | ||
holder.binding.textViewItemNciDumpMessage.setText(dataText.toString()); | ||
} | ||
|
||
public abstract static class AbsNciDumpAdapter extends RecyclerView.Adapter<NciDumpViewHolder> { | ||
@NonNull | ||
@Override | ||
public NciDumpViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||
cc.ioctl.nfcdevicehost.databinding.ItemMainNciDumpBinding binding = cc.ioctl.nfcdevicehost.databinding.ItemMainNciDumpBinding.inflate(LayoutInflater.from(parent.getContext()), | ||
parent, false); | ||
return new NciDumpViewHolder(binding, NciDumpViewHolder.ViewType.TRANSACTION); | ||
} | ||
} | ||
|
||
} |
130 changes: 130 additions & 0 deletions
130
app/src/main/java/cc/ioctl/nfcdevicehost/activity/ui/dump/HalDumpFileViewFragment.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package cc.ioctl.nfcdevicehost.activity.ui.dump; | ||
|
||
import android.app.Activity; | ||
import android.content.ContentResolver; | ||
import android.content.Context; | ||
import android.net.Uri; | ||
import android.os.Bundle; | ||
import android.view.LayoutInflater; | ||
import android.view.View; | ||
import android.view.ViewGroup; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
import androidx.appcompat.app.AlertDialog; | ||
import androidx.recyclerview.widget.DefaultItemAnimator; | ||
import androidx.recyclerview.widget.DividerItemDecoration; | ||
import androidx.recyclerview.widget.LinearLayoutManager; | ||
import androidx.recyclerview.widget.RecyclerView; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.ArrayList; | ||
|
||
import cc.ioctl.nfcdevicehost.R; | ||
import cc.ioctl.nfcdevicehost.activity.MainUiFragmentActivity; | ||
import cc.ioctl.nfcdevicehost.daemon.INciHostDaemon; | ||
import cc.ioctl.nfcdevicehost.databinding.FragmentMainDumpBinding; | ||
import cc.ioctl.nfcdevicehost.decoder.NxpHalV2EventTranslator; | ||
import cc.ioctl.nfcdevicehost.util.ThreadManager; | ||
|
||
/** | ||
* Fragment for showing a dump file. | ||
* <p> | ||
* Host Activity may be {@link MainUiFragmentActivity} | ||
* or {@link cc.ioctl.nfcdevicehost.activity.SidebandHostActivity} | ||
*/ | ||
public class HalDumpFileViewFragment extends BaseHalDumpFragment { | ||
|
||
public static final String EXTRA_CONTENT = HalDumpFileViewFragment.class.getName() + ".EXTRA_CONTENT"; | ||
|
||
private String mRawSerializedDumpData = null; | ||
private ArrayList<INciHostDaemon.IoEventPacket> mRawIoEvents = null; | ||
private ArrayList<NxpHalV2EventTranslator.TransactionEvent> mTransactionEvents = null; | ||
|
||
private FragmentMainDumpBinding mBinding; | ||
private final AbsNciDumpAdapter mDumpAdapter = new AbsNciDumpAdapter() { | ||
@Override | ||
public void onBindViewHolder(@NonNull NciDumpViewHolder holder, int position) { | ||
NxpHalV2EventTranslator.TransactionEvent event = mTransactionEvents.get(position); | ||
updateListViewItem(holder, event); | ||
} | ||
|
||
@Override | ||
public int getItemCount() { | ||
return mTransactionEvents == null ? 0 : mTransactionEvents.size(); | ||
} | ||
}; | ||
|
||
@Nullable | ||
@Override | ||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, | ||
@Nullable Bundle savedInstanceState) { | ||
Context context = inflater.getContext(); | ||
mBinding = FragmentMainDumpBinding.inflate(inflater, container, false); | ||
LinearLayoutManager layoutManager = new LinearLayoutManager(context); | ||
RecyclerView recyclerView = mBinding.recyclerViewMainFragmentDumpList; | ||
recyclerView.setLayoutManager(layoutManager); | ||
layoutManager.setOrientation(RecyclerView.VERTICAL); | ||
recyclerView.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL)); | ||
recyclerView.setItemAnimator(new DefaultItemAnimator()); | ||
mBinding.recyclerViewMainFragmentDumpList.setAdapter(mDumpAdapter); | ||
// load data file | ||
Bundle args = getArguments(); | ||
final String uriPath = args == null ? null : args.getString(EXTRA_CONTENT); | ||
if (uriPath == null) { | ||
requireActivity().finish(); | ||
return null; | ||
} | ||
Uri uri = Uri.parse(uriPath); | ||
final ContentResolver resolver = requireContext().getContentResolver(); | ||
ThreadManager.async(() -> { | ||
try { | ||
InputStream is = resolver.openInputStream(uri); | ||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
byte[] buffer = new byte[1024]; | ||
int len; | ||
while ((len = is.read(buffer)) > 0) { | ||
baos.write(buffer, 0, len); | ||
} | ||
is.close(); | ||
mRawSerializedDumpData = baos.toString(); | ||
try { | ||
mRawIoEvents = NxpHalV2EventTranslator.loadIoEventPacketsFromString(mRawSerializedDumpData); | ||
NxpHalV2EventTranslator translator = new NxpHalV2EventTranslator(); | ||
translator.pushBackRawIoEvents(mRawIoEvents); | ||
mTransactionEvents = translator.getTransactionEvents(); | ||
ThreadManager.runOnUiThread(() -> mDumpAdapter.notifyDataSetChanged()); | ||
} catch (RuntimeException e) { | ||
ThreadManager.runOnUiThread(() -> new AlertDialog.Builder(context) | ||
.setTitle(R.string.ui_dialog_error_title) | ||
.setMessage(e.toString()) | ||
.setCancelable(false) | ||
.setPositiveButton(android.R.string.ok, (dialog1, which) -> requireActivity().finish()) | ||
.show()); | ||
} | ||
} catch (IOException e) { | ||
ThreadManager.runOnUiThread(() -> new AlertDialog.Builder(context) | ||
.setTitle(R.string.ui_dialog_error_title) | ||
.setMessage(context.getString(R.string.ui_dialog_unable_to_open_file_v0s, uri.toString()) | ||
+ "\n" + e) | ||
.setCancelable(false) | ||
.setPositiveButton(android.R.string.ok, (dialog1, which) -> requireActivity().finish()) | ||
.show()); | ||
} | ||
}); | ||
return mBinding.getRoot(); | ||
} | ||
|
||
@Override | ||
public void onResume() { | ||
super.onResume(); | ||
Activity activity = requireActivity(); | ||
activity.setTitle(R.string.ui_title_main_dump); | ||
if (activity instanceof MainUiFragmentActivity) { | ||
((MainUiFragmentActivity) activity).hideFloatingActionButton(); | ||
} | ||
|
||
} | ||
} |
Oops, something went wrong.